TIL(13) 디자인패턴
# 디자인 패턴
> 디자인 패턴은 실제 개발 현장에서 비즈니스 요구 사항을 프로그래밍으로 처리하면서 만들어진 다양한 해결책 중에서 많은 사람들이 인정한 베스트 프랙티스를 정리한 것이다.
> 디자인 패턴은 객체 지향 특성과 설계 원칙을 기반으로 구현돼 있다.
> 소프트웨어를 설계할 때 특정 맥락에서 자주 발생하는 고질적인 문제들이 또 발생했을 때 재사용할 할 수있는 훌륭한 해결책
> “바퀴를 다시 발명하지 마라(Don’t reinvent the wheel)”
# GoF 디자인 패턴
> 23가지의 디자인 패턴을 정리하고 생성(Creational), 구조(Structural), 행위(Behavioral) 3가지로 분류했다.
# GoF 디자인 패턴의 분류
> 생성(Creational) 패턴
객체 생성에 관련된 패턴으로 객체의 생성과 조합을 캡슐화해 특정 객체가 생성되거나 변경되어도 프로그램 구조에 영향을 크게 받지 않도록 유연성을 제공한다.
> 구조(Structural) 패턴
클래스나 객체를 조합해 더 큰 구조를 만드는 패턴이다. 예를 들어 서로 다른 인터페이스를 지닌 2개의 객체를 묶어 단일 인터페이스를 제공하거나 객체들을 서로 묶어 새로운 기능을 제공하는 패턴이다.
> 행위(Behavioral)
객체나 클래스 사이의 알고리즘이나 책임 분배에 관련된 패턴으로 한 객체가 혼자 수행할 수 없는 작업을 여러 개의 객체로 어떻게 분배하는지, 또 그렇게 하면서도 객체 사이의 결합도를 최소화하는 것에 중점을 둔다.
l
1. 어댑터 패턴
2. 프록시 패턴
3. 데코레이터 패턴
4. 싱글턴 패턴
5. 템플릿 메서드 패턴
6. 팩터리 메서드 패턴
7. 전략 패턴
8. 템플릿 콜백 패턴
# 어댑터 패턴
"호출당하는 쪽의 메서드를 호출하는 쪽의 코드에 대응하도록 중간에 변화기를 통해 호출하는 패턴"
어댑터를 변역하면 변환기라고 할 수 있다. 변환기의 역할은 서로 다른 두 인터페이스 사이에 통신이 가능하게 하는 것이다.
public class ServiceA {
void runServiceA(){
System.out.println("Service A");
}
}
public class ServiceB {
void runServiceB(){
System.out.println("Service B");
}
}
/*
public class NoAdapter {
public static void main(String args[]){
ServiceA serviceA = new ServiceA();
ServiceB serviceB = new ServiceB();
serviceA.runServiceA();
serviceB.runServiceB();
}
}
*/
ServiceA와 ServiceB의 메서드를 runService( )라고 하는 같은 이름의 메서드로 호출해서 사용할 수있다. 클라이언트가 변환기를 통해 runService( )라는 동일한 메서드명으로 두 개체의 메서드를 호출하는 것을 볼 수 있따.
어댑터 패턴은 합성, 즉 객체를 속성으로 만들어서 참조하는 디자인 패턴으로 한 문장으로 요약하면 다음과 같다.
# 프록시 패턴
"제어 흐름을 조정하기 위한 목적으로 중간에 대리자를 두는 패턴"
프록시는 대리자, 대변인이라는 뜻을 가진 단어이다.
프록시 패턴의 경우 실제 서비스 객체가 가진 메서드와 같은 이름의 메서드를 사용한데, 이를 위해 인터페이스를 사용한다. 인터페이스를 사용하면 서비스 객체가 들어갈 자리에 대리자 객체를 대신 투입해 클라이언트 쪽에서는 실제 서비스 객체를 통해 메서드를 호출하고 반환값을 받는지, 대리자 객체를 통해 메서드를 호출하고 반환값을 받는지 전혀 모르게 처리할 수도 있다.
> 프록시 패턴의 중요 포인트
1. 대리자는 실제 서비스와 같은 이름의 메서드를 구현한다. 이때 인터페이스를 사용한다.
2. 대리자는 실제 서비스에 대한 참조 변수를 갖는다.
3. 대리자는 실제 서비스의 같은 이름을 가진 메서드를 호출하고 그 값을 클라이언트에게 돌려준다.
4. 대리자는 실제 서비스의 메서드 호출 전후에 별도의 로직을 수행할 수도 있다.
청와대 대변인, 백악관 대변인은 해당 기관의 입장을 대변할 뿐 그 입장에 자신의 입장을 가감하지 않는다. 프록시 패턴역시 메서드의 반환값에 가감하는 것을 목적으로 하지 않고 제어 흐름을 변경하거나 다른 로직을 수행하기 위해 사용한다.
프록시 패턴은 OCP와 DIP 원칙이 적용된 패턴이다. 함께 이해하기! < 하면 삭제
# 데코레이터 패턴
"메서드 호출의 반환값에 변화를 주기 위해 중간에 장식자를 두는 패턴"
데코레이터는 도장/도배업자를 의미한다. 데코레이터 패턴은 프록시 패턴과 구현 방법이 같다. 다만 프록시 패턴은 클라이언트가 최종적으로 돌려 받는 반환값을 조작하지 않고 그대로 전달하는 반면 데코레이터 패턴은 클라이언트가 받는 반환값에 장식을 덧입힌다.
# 싱글턴 패턴
"클래스의 인스턴스, 즉 객체를 하나만 만들어 사용하는 패턴"
싱글턴 패턴이란 인스턴스를 하나만 만들어 사용하기 위한 패턴이다. 커넥션 풀, 스레드 풀, 디바잉스 설정 객체등과 같은 경우 인스턴스를 여러 개 만들게 되면 불필요한 자원을 사용하게 되고, 또 프로그램이 예상치 못한 결과를 낳을 수 있다. 싱글턴 패턴은 오직 인스턴스를 하나만 만들고 그것을 계속해서 재사용한다. 싱글턴 패턴은 의미상 두 개의 객체가 존재할 수 없다. 싱글턴 패턴을 구현하려면 객체 생성에 제약을 걸어야 한다.
# 구현 방법
1. new를 실행할 수 없도록 생성자에 private 접근 제어자를 지정한다.
private 생성자이기에 외부 클래스나 메인 클래스에서 new를 이용한 객체를 생성할 수 없다.
2. 유일한 단일 객체를 반환할 수 있는 정적 메서드가 필요하다.
3. 유일한 단일 객체를 참조할 정적 참조 변수가 필요하다
# T메모리
s1, s2, s3 참조 변수가 힙 영역의 하나의 단일 객체를 참조한다. 이것이 바로 싱글턴 패턴의 힘이다. 단일 객체인 경우 결국 공유 객체로 사용되기 때문에 속성을 갖지 않는 것이 정석이다. 단일 객체가 속성을 갖게 되면 하나의 참조 변수가 변경한 단일 객체의 속성이 다른 참조 변수에 영향을 미친다. 다만 읽기 전용 속성을 갖는 것은 문제가 되지 않는다. 또한 단일 객체가 다른 단일 객체에 대한 참조를 속성으로 가지는 것 또한 문제가 되지 않는다.
스프링의 싱글턴 빈이 가져야 할 제약 조건이기도 하다. < 알아보고 삭제!
싱글턴 패턴을 통해 메모리를 절약시킬수 있을 뿐만 아니라, 객체를 생성하는 시간을 없앨수 있기 때문에 성능적인 측면에서도 도움이 된다.
# 특징
1. private 생성자를 갖는다.
2. 단일 객체 참조 변수를 정적 속성으로 갖느다.
3. 단일 객체 참조 변수가 참조하는 단일 객체를 반환하는 getInstance( ) 정적 메서드를 갖느다.
4. 단일 객체는 쓰기 가능한 속성을 가지 않는 것이 정석이다.
# 템플릿 메서드 패턴
"상위 클래스의 견본 메서드에서 하위클래스가 오버라이딩한 메서드를 호출하는 패턴"
public class dog{
public void playWithOwner(){
System.out.println("aaaaaaaaaaaaaa");
System.out.println("bbbbbbbbbbbbbb");
System.out.println("Main A");
System.out.println("cccccccccccccc");
}
}
public class cat{
public void playWithOwner(){
System.out.println("aaaaaaaaaaaaaa");
System.out.println("bbbbbbbbbbbbbb");
System.out.println("Main B");
System.out.println("cccccccccccccc");
}
}
위 코드를 보면 a, b, c 부분이 동일하며 코드가 중복되는 문제를 파악할 수 있다. 우리는 상속을 통해 동일한 부분은 상위 클래스로, 달라지는 부분만 하위 클래스로 분할할 수 있다.
(책 p.217 참조 혹은 후에 재업로드)
즉, 템플릿 메서드 패턴은 상위 클래스에 공통 로직을 수행하는 템플릿 메서드와 하위 클래스에 오버라이딩을 강제하는 추상메서드 또는 선택적으로 오버라이딩할 수 있는 훅 메서드를 두는 패턴을 의미한다.
# 팩터리 메서드 패턴
# 전략 패턴
"클라이언트가 전략을 생성해 전략을 실행할 컨텍스트에 주입하는 패턴"
전략 패턴은 객체의 꽃이라고도 불린다. 전략 패턴은 템플릿 메서드 패턴과 유사하다. 차이점이 있다면 템플릿 메서드 패턴은 상속을 이용하여 문제를 해결하지만 전략패턴은 객체 주입을 통해 문제를 해결한다.
즉, 객체를 생성하며 매게 변수에 객체를 집어 넣는 것이 객체 주입이라고 생각한다. 이를 통해 코드 중복을 피하고 재사용하여 코드를 작성할수 있는 것이 중요하다고 생각한다.
https://gmlwjd9405.github.io/2018/08/10/composite-pattern.html
[Design Pattern] 컴퍼지트 패턴이란 - Heee's Development Blog
Step by step goes a long way.
gmlwjd9405.github.io