2주차 코드잇 위클리 페이퍼

"개인 학습 기록용"

Q.객체지향 프로그래밍에서 '단일 책임 원칙(SRP)과 '개방-폐쇄 원칙(OCP)'에 대해서 설명하고, 각각의 원칙을 적용한 코드 예시.

단일 책임 원칙(SRP, Single Responsibility Principle)은

객체지향 프로그래밍에서 매우 중요한 원칙 중 하나이다.
SRP는 소프트웨어 공학의 대표적인 객체지향 설계 원칙 중 하나로, "SOLID 원칙"의 설계 원칙 집합 중 첫번째 원칙이다.

단일 책임 원칙이란,
"모든 클래스는 단 한 가지의 책임만을 가지고, 그 안에 정의된 모든 기능은 이 한 가지 책임을 수행하는 데 집중되어야 한다." 라는 뜻이다.
쉽게 말하면, 하나의 클래스로 너무 많은 일을 하지 말고, 한가지 책임(역할)만 담당하게 하라는 것이다.

이 원칙을 지키면 코드를 수정하거나 확장할 때 훨씬 더 수월해진다.
왜냐하면 각각의 책임이 분리되어 있기 때문에, 한 부분을 변경해도 다른 부분에 영향을 주지 않기 때문이다.

  • 좋은예:

    class UserManager {
    	void addUser(User user) {...}
       void removeUser(User user) {...}
       }

    -> 사용자 관리 책임만 가지고 있다.

  • 나쁜예:

    class UserManager {
    	void addUser(User user) {...}
       void removeUser(User user) {...}
       void sendEmail(User user, String message) {...}
       void printUserList() {...}
       }

    -> 사용자 관리뿐 아니라 이메일 보내기, 출력 등 여러 책임을 같이 가지게 돼서 단일 책임 원칙을 어긴다.
    단일 책임 원칙을 지키면 프로그램이 커져도 유지보수와 수정이 편해진다는 장점이 있다.

    참고로 책임의 범위가 어디까지인가 하는 것은 상황마다 조금씩 다를수 있으니, 항상 여러 책임이 한 클래스에 섞여 있지 않은지 고민해 보는 습관을 가지면 좋다.

개방-폐쇄원칙(OPC)란,

개방 폐쇄 원칙(OCP, Open-Closed Principle)은 객체지향 프로그래밍의 설계 원칙, SOLID의 두번째 원칙이다.

개뱅 폐쇄 원칙의 정의는 "클래스는 확장에는 열려 있어야 하고, 수정에는 닫혀 있어야 한다"이다.
조금 더 쉽게 설명하면,

  • 기존에 있는 코드를 수정하지 않고(=닫혀있음)
  • 새로운 기능이나 요구사항을 확장할 수 있도록(=열려있음) 코드를 작성하라는 뜻이다.

이 원칙을 지켠,

  • 기존 로직에 영향 없이 새로운 기능을 추가할 수 있고,
  • 여러 개발자가 동시에 코드를 수정하지 않아 협업도 쉬워진다.
    주로 추상 클래스나 인터페이스를 이용해서 이 원칙을 달성한다.

예시:

# 추상 클래스(혹은 인터페이스)
class Keyboard:
	def inpu(self, key):
    	pass

# 확장: 새로운 키보드 클래스 추가
class AppleKeyboard(Keyboard):
	def input(self, key):
    	# 애플 키보드 입력 방식 구현
        pass
        
class SamsungKeyboard(Keyboard):
	def input(self, key):
    	# 상성 키보드 입력 방식 구현
		pass
 # 기존 클래스는 수정하지 않고, 새로운 키보드만 추가(확장)함.

이렇게 하면, 새로운 키보드가 필요할 때 기존 코드는 건드리지 않고(수정 X) 새로운 클래스를 추가 (확장) 하기만 하면된다.

Q.Stream API의 map과 flatMap의 차이점을 설명하고, 각각의 활용 사례를 예시 코드와 함께 설명.

  1. map과 flatMap의 차이점
  • map
    각 요소를 하나씩 변환(매핑)해서 새로운 요소로 만들어주는 연산이다.
    즉, 입력된 각 요소를 함수에 넣어서 한 요소를 다른 요소로 바꿔주는 것이다.
  • flatMap
    각 요소를 변환할 때 여러 개(혹은 0개)의 요소로 만들어주고, 이걸 한줄로 평평하게(flatten)펼쳐서 하나의 stream으로 만들어 주는 연산이다.
    즉,
  • map: A->B, A->b, A->B (1:1변환)
  • flatMap: A -> [B,C],A -> [],A -> [D](1:N 변환 & 펼치기)
  1. map과 flatMap의 활용 사례와 예제
    map 예시
    목표: 문자열 리스트를 받아서, 모두 대문자로 변환하기
import java.util.*;
import java.util.stream.*;

public class MapExample{
	public static void main(String[] args) {
    List<String> names = Arrays.asList("codeit", "guru", "java");
    List<String> upperNames = names.stream()
    	.map(name -> name.toUpperCase())
        .collect(Collectors.toList());
        
    System.out.println(upperNames); // [CODEIT, GURU, JAVA]
   }
}
  • 설명
    리스트의 각 문자열 하나씩을 함수(toUpperCase)에 넣어서, 각각 대문자로 바꾼 리스트를 만든다.

flatMap 예시
목표: 여러 문장으로 이루어진 문자열 리스트에서, 모든 단어를 하나의 리스트로 만들기

import java.util.*;
import java.util.stream.*;

public class FlatMapExample {
	public static void main(String[] args) {
    	List<String> sentences = Arrays.asList("Codeit is good", "Java is fun");
        List<String> words = sentences.stream()
        	.flatMap(sentence -> Arrays.stream(sentence.split(" ")))
            .collect(Collectors.toList());
            
       System.out.println(words); //[Codeit, is, good, Java, is fun]
       }
 }      
  • 설명
    문자열 한 개는 split()하면 단어 배열이 되고(여러 값으로 쪼개짐).
    이걸 flatMap으로 펼쳐서 모든 단어를 하나의 리스트로 만든다.
  1. 정리
  • map : 각 요소 -> 변형된 하나의 요소로 매핑(1:1)
  • flatMap : 각 요소 -> 여러 요소로 매핑 후, 한줄로 펼침(1:N)

활용법

  • map: 단순값 변환(대문자, 길이, 객체 등)
  • flatMap: 리스트 안의 리스트, 문장 안의 단어 등 내부에 여러 값이 들어 있는 경우에 분해해서 한 줄로 쫙 펼치고 싶을 때 씁니다.

참고 :
https://www.baeldung.com/solid-principles
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#map-java.util.function.Function-
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#flatMap-java.util.function.Function-

profile
Rookie

0개의 댓글