일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- .env
- 멀티뷰
- 스프링 환경변수
- 채팅방 구현
- android studio
- 스프링 시큐리티 없이
- jpa bulk insert
- 중간 테이블 엔티티 최적화
- oauth 로그인
- jpa 최적화
- hibe
- 쿠버네티스 #fabric8
- mysql multi-row insert
- jpa dto 매핑
- 로또 앱 만들기
- 로또 등수 알고리즘
- 리사이클러뷰
- 스프링 환경변수 설정
- 스프링 소셜 로그인
- multiview
- 스프링 오어스
- 리사이클러뷰 멀티뷰
- 뷰 페이징
- 로또 등수 코드
- 안드로이드 스튜디오
- Androoid Studio
- spring 채팅방
- java
- 쿠버네티스
- springboot
- Today
- Total
야미의 개발
[JAVA]자바와 객체지향(4) - 스프링이 사랑한 디자인 패턴 본문
이 도서를 기반으로 글을 작성하였습니다.
1.어댑터 패턴
서로 다른 두 인터페이스의 통신을 가능하게 하는 디자인 패턴
기존의 클래스를 수정하지 않고도, 특정 인터페이스를 필요로하는 코드에서 사용할 수 있게 합니다.
구성요소로는 타켓, 어댑티, 어댑터, 클라이언트가 있으며, 타겟은 클라이언트가 직접적으로 호출하는 인터페이스를 말하며, 어댑티는 아직 호환되지 않은 기존 클래스를 의미합니다. 클라이언트는 특정작업을 요청하는 클래스, 어댑터는 타겟 인터페이스를 구현하여 클라이언트 요청을 어댑티로 전달하는 클래스입니다.
구성요소 정리
- Client: 실제 기능을 사용하는 클라이언트입니다. 이 클라이언트는 Target Interface를 통해 어댑터와 소통합니다.
- Target Interface: 클라이언트가 사용하려는 인터페이스를 정의합니다. 클라이언트는 이 인터페이스를 통해 어댑터와 소통하며 기능을 사용합니다.
- Adapter: 어댑터는 Target Interface를 구현하면서, 내부에서 실제 기능을 수행하는 Adaptee를 감싸고 있습니다. 이 어댑터를 통해 클라이언트는 Adaptee의 기능을 사용할 수 있게 됩니다.
- Adaptee: 실제로 기능을 수행하는 클래스입니다. 클라이언트가 직접 사용하기 어려운 인터페이스를 가지고 있을 수 있습니다.
어댑터 패턴의 구현 예시
- 자바의 I/O라이브러리
- 스프링의 핸들러 어댑터 - https://ksabs.tistory.com/250 참고
간단한 어댑터 패턴 구현 방법
1. 호환되지 않는 Target이라는 인터페이스와, Target을 사용하고 싶은 Adaptee
2. Adapter 클래스에서 Target 인터페이스를 구현하며, Adaptee 클래스를 멤버 변수로 가지고 있음 - target의 메서드를 Adaptee가 사용가능하게 오버라이딩
3. 구현체로 Adapter를 사용하여 Target 타입의 객체 참조 변수를 선언하여 오버라이딩된 메소드를 통해
Adaptee 클래스의 기능을 Adaptee 클래스를 직접 호출하지 않고도 사용가능
->> 클라이언트에서 이미 target 인터페이스를 사용하고 있으니 쓰고싶은 Adaptee 클래스의 기능을 사용하기 위해 target 인터페이스를 구현하며, Adaptee 클래스의 내용을 변환한
Adapter라는 클래스를 만들어 여전혀 target 인터페이스를 클라이언트에서 사용할 수 있게함!
장단점
장점 : 클래스를 수정하지 않고도 클라이언트에서 새로운 인터페이스 사용가능
단점 : 소스코드 증가, 코드 복잡성 증가, 유지보수 어려움, 추가적인 처리 시간 or 오버헤드 발생 가능
한줄 요약
호출당하는 쪽의 메서드를 호출하는 쪽의 코드에 대응하도록 중간에 변환기를 호출하는 패턴
https://yozm.wishket.com/magazine/detail/2077/
자바 어댑터 패턴은 어떻게 쓰일까? | 요즘IT
이번 글을 시작으로 실제 자바 프로젝트에서 디자인 패턴을 어떻게 사용하는지 정리하고자 합니다. 첫 번째 주제로 자바 라이브러리와 프레임워크에서 자주 쓰이는 어댑터 패턴(Adapter Pattern)에
yozm.wishket.com
2. 프록시 패턴
프록시는 대리인, 대리자입니다.
프록시 패턴은 대상 객체에 접근하기 전, 그 접근에 대한 흐름을 가로채 접근을 필터링 하거나 수정하는 등의 역할을 하는 계층이 있는 디자인 패턴입니다.
보안, 데이터 검증, 캐싱, 로깅 등에 사용하며 프록시 서버로도 활용됩니다.
챗 지피티에서 프록시 패턴의 코드를 간단히 만들어 달라 부탁해봤습니다
// 주체(Subject) 인터페이스
public interface Image {
void display();
}
// 실제 주체(RealSubject) 클래스
public class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadImageFromDisk();
}
private void loadImageFromDisk() {
System.out.println("Loading image: " + filename);
}
@Override
public void display() {
System.out.println("Displaying image: " + filename);
}
}
// 프록시(Proxy) 클래스
public class ImageProxy implements Image {
private RealImage realImage;
private String filename;
public ImageProxy(String filename) {
this.filename = filename;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(filename);
}
realImage.display();
}
}
// 프록시 패턴 사용예시
public class ProxyPatternExample {
public static void main(String[] args) {
// 실제 주체에 직접 접근
Image realImage = new RealImage("realImage.jpg");
realImage.display();
System.out.println("------------");
// 프록시를 통한 접근
Image proxyImage = new ImageProxy("proxyImage.jpg");
proxyImage.display();
}
}
위의 예시에서는 Image 인터페이스를 정의하고, 이를 구현하는 실제 주체 클래스 RealImage를 만들고
ImageProxy를 사용하여 접근을 간접적으로 제한하는 코드입니다.
프록시 패턴을 이용하여 실제로 이미지를 사용할때까지 이미지 로딩을 연기하거나 다른 제어를 할 수 있는 코드 입니다.
프록시 패턴에 대해 알아두어야 할점!
- 대리자는 실제 서비스와 같은 이름의 메서드를 구현한다. 이때 인터페이스를 사용한다.
- 동일한 인터페이스 구현으로 클라이언트에게 동일한 메서드를 제공함 - 대리자는 실제 서비스에 대한 참조변수를 갖는다(합성).
- 합성- has-a 관계로 Proxy 클래스가 실제 서비스 객체를 내부에 보유하고 있는 것을 의미 - 대리자는 실제 서비스의 같은 이름을 가진 메소드를 호출하고 클라이언트에게 돌려준다.
- 대리자는 실제 서비스의 메서드 호출 전후에도 별도의 로직을 수행할 수 있다.
- 이를 통해 보안, 로킹, 캐싱 등 다양한 부가기능 제공
++
프록시 서버에서의 캐싱
캐시 안에 정보를 담아두고, 캐시 안에있는 정보를 요구하는 요청에 대해 다시 저 멀리있는 원격서버에 요청하지 않고 캐시안에있는 데이터를 활용 -> 불필요한 외부와의 연결을 줄여 트래픽을 줄일 수 있음
프록시 서버
서버와 클라이언트 사이에서 클라이언트가 자신을 통해 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해주는 컴퓨터 시스템이나 응용 프로그램
제어의 흐름을 조정하기 위한 목적으로 중간에 대리자를 두는 패턴
3. 데코레이터 패턴
데코레이터는 도장/ 도배업자를 의미합니다.
데코레이터 패턴은 원본에 장식을 더하는 패턴이라는 뜻입니다. 프록시와 구현방법은 동일하나 프록시는 동일한 반환값을 돌려주는 반면, 데코레이터는 클라이언트가 받는 반환값에 장식을 덧입힙니다.
데코레이터 패턴의 구성요소
- Component(컴포넌트) 인터페이스 또는 추상 클래스
실제 컴포넌트와 데코레이터가 구현할 인터페이스를 정의합니다. - ConcreteComponent(구체적인 컴포넌트) 클래스
실제 기능을 제공하는 구체적인 컴포넌트입니다. - Decorator(데코레이터) 클래스
컴포넌트의 인터페이스를 구현하고, 구체적인 컴포넌트를 감싸서 새로운 기능을 추가하는 역할을 합니다. - ConcreteDecorator(구체적인 데코레이터) 클래스
실제로 새로운 기능을 추가하는 구체적인 데코레이터 클래스입니다.
반환값에 장식을 더한다는 것만 빼면 위의 프록시 패턴과 동일합니다.
메서드 호출의 반환값에 변화를 주기위해 중간에 장식자를 두는 패턴
4. 싱글톤 패턴
하나의 클래스에 하나의 인스턴스만 가지는 패턴
보통 하나의 클래스를 기반으로 여러 개의 개별 인스턴스를 만들 수 있지만, 그렇게하지 않고 단 하나의 인터페이스를 만들어 이를 기반으로 로직을 만드는데 쓰이며 데이터베이스 연결 모듈에 많이 사용함
하나의 인스턴스를 다른 모듈이 공유하며 사용하기 때문에 인스턴스 생성 비용 줄어들음
but, 의존성이 높아지는 단점이 있습니다.
싱글톤 패턴의 기본 요소
1. new를 실행할 수 없도록 생성자에 private 접근 제어자 지정
2. 유일한 단일 객체를 반환할 수 있는 정적 메소드 필요
3. 유일한 단일 객체를 참조할 정적 참조변수가 필요
class Singleton{
private static class singleInstanceHolder{
private static class final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return singleInstanceHolder.INSTANCE;
}
}
public class HelloWorld{
public static void main(String[] args){
Singleton a = Singleton.getInstance();
Singleton b = Singleton.getInstance();
System.out.println(a.hashCode());
System.out.println(b.hashCode());
if(a==b){
System.out.println(true);
}
}
}
싱글톤의 주의점
1. private 생성자를 가짐
2. 단일 객체이므로 속성값을 갖지 않아야합니다. -> Stateless, 무상태로 설계
3. 단일 객체는 참조변수를 정적 속적으로 가짐 / static
4. 단일 객체 참조 변수가 참조하는 단일 객체를 반환하는 getInstance() 정적 메서드를 갖는다
싱글톤의 단점
TDD 테스트가 불가능
TDD에서는 단위테스트를 주로 하는데 단위테스트는 서로 독립적이어야하고 테스트를 어떤 순서로든 할 수 있어야합니다. 테스트에서 독립적인 인스턴스를 만들어야하는데, 주로 싱글톤 클래스에서는 생성자가 private이기 때문에 외부에서 직접 객체를 생성할 수 없고, 해당 클래스의 유일한 인스턴스는 정적 상수 또는 메서드를 통해 얻을 수 있습니다.
-> 해결법
1. @Before @After 어노테이션을 통해 각 테스트 메서드 실행전에 싱글톤 인스턴스를 초기화하거나 테스트가 끝난후에 초기 상태로 되돌려 놓을 수 있습니다.
2. DI 를 통해 Mock(가짜) 객체를 주입하여 독립적인 테스트 진행
한편 싱글톤 패턴의 문제인 구체적인 구현 클래스에 의존하는 것과, 생성자를 private으로 하여 기본 생성자를 막아 상송을 불가능하게 하는 문제, 그리고 코드의 복잡성 증가는 - 스프링에서 제공하는 싱글턴을 이용해 해결가능합니다.
클래스의 인스턴스, 즉 객체를 하나만 만들어 사용하는 패턴
5. 템플릿 메서드
여러 클래스에서 공통으로 사용하는 메서드를 템플릿화 하여 상위 클래스에서 정의하고, 하위 클래스마다 세부 동작을 다르게 구현하는 패턴이다.
변하지 않는 기능 : 상위 클래스에 정의
자주 변경되거나 확장할 기능 : 하위 클래스에 정의
hook 메서드
훅(hook) 메소드는 부모의 템플릿 메서드의 영향이나 순서를 제어하고 싶을때 사용되는 메서드 형태를 말합니다.
-> 훅 메소드는 추상 메소드가 아닌 일반 메소드로 구현하는데, 선택적으로 오버라이드 하여 자식 클래스에서 제어하거나 아니면 놔두거나 하기 위해서입니다.
import java.io.File;
// 파일 처리를 위한 AbstractClass
abstract class FileProcessor {
// 템플릿 메서드
public final void processFile(String filePath) {
File file = openFile(filePath);
String content = readFileContent(file);
String processedContent = processContent(content);
saveProcessedContent(processedContent);
closeFile(file);
}
// 훅 메서드 (파일 열기, 디폴트 구현 제공)
File openFile(String filePath) {
System.out.println("Opening file: " + filePath);
return new File(filePath);
}
// 훅 메서드 (파일 읽기, 추상 메서드)
abstract String readFileContent(File file);
// 훅 메서드 (컨텐츠 처리, 디폴트 구현 제공)
String processContent(String content) {
System.out.println("Default content processing");
return content;
}
// 훅 메서드 (처리된 컨텐츠 저장, 추상 메서드)
abstract void saveProcessedContent(String content);
// 훅 메서드 (파일 닫기, 디폴트 구현 제공)
void closeFile(File file) {
System.out.println("Closing file: " + file.getName());
}
}
// FileProcessor를 상속받은 ConcreteFileProcessor
class ConcreteFileProcessor extends FileProcessor {
// 파일 읽기 메서드 오버라이딩
@Override
String readFileContent(File file) {
System.out.println("Reading content from file: " + file.getName());
// 실제 파일 읽기 로직은 여기에 구현될 것이다.
return "File content goes here.";
}
// 처리된 컨텐츠 저장 메서드 오버라이딩
@Override
void saveProcessedContent(String content) {
System.out.println("Saving processed content to a new file.");
// 실제 파일 저장 로직은 여기에 구현될 것이다.
}
// 훅 메서드 (컨텐츠 처리, 오버라이딩)
@Override
String processContent(String content) {
System.out.println("Custom content processing");
// 실제 컨텐츠 처리 로직은 여기에 구현될 것이다.
return "Processed: " + content;
}
}
public class FileProcessorExample {
public static void main(String[] args) {
// ConcreteFileProcessor의 객체 생성
ConcreteFileProcessor fileProcessor = new ConcreteFileProcessor();
// 템플릿 메서드 호출
fileProcessor.processFile("example.txt");
}
}
위에서처럼
public final void processFile(String filePath) {
File file = openFile(filePath);
String content = readFileContent(file);
String processedContent = processContent(content);
saveProcessedContent(processedContent);
closeFile(file);
}
의 부분을 통해 processFile라는 템플릿 메소드를 통해 파일을 처리하는 흐름을 고정하며,
File openFile(String filePath) {
System.out.println("Opening file: " + filePath);
return new File(filePath);
}
와 같이 일반 메소드로 작성되어 기본형을 제공해주는 hook 메소드를 확인 할 수 있습니다.
또한 반드시 구현해야하는 추상메서드
abstract String readFileContent(File file);
도 확인 가능합니다.
템플릿의 장단점
장점: 클라이언트가 대규모 알고리즘의 특정 부분만 재정의 하도록하여 알고리즘의 다른 부분에 발생하는 변경사항의 영향을 덜받도록, 상위 추상클래스로 로직을 공통화하여 코드 중복 줄임, 서브 클래스의 역할을 줄이여 관리 용이
단점: 고정된 골격으로 인해 유연성 제한될 수 있음, 알고리즘 구조가 복잡할수록 템플릿 로직 형태 유지가 어려움, 상위 클래스를 수정할때 하위 클래스들의 수정이 필요할 수도 있음
이처럼 상위 클래스에 공통 로직을 수행하는 템플릿 메서드와 하위 클래스에서 오버라이딩을 강제하는 추상메서드 또는 선택적으로 오버라이딩 할 수 있는 훅 메서드를 두는 패턴을 템플릿 패턴이라 합니다.
상위 클래스의 견본 메서드에서 하위 클래스가 오버라이딩한 메서드를 호출하는 패턴
💠 템플릿 메소드(Template Method) 패턴 - 완벽 마스터하기
Template Method Pattern 템플릿 메서드(Template Method) 패턴은 여러 클래스에서 공통으로 사용하는 메서드를 템플릿화 하여 상위 클래스에서 정의하고, 하위 클래스마다 세부 동작 사항을 다르게 구현하
inpa.tistory.com
6. 팩토리 패턴
팩토리 패턴에서 팩토리는 공장에서 물건을 만드는 것을 의미합니다. 따라서 팩토리 메서드는 객체를 생성하고 반환하는 메서드입니다.
이때 패턴이붙은 것은 하위 클래스에서 팩토리 메서드를 오버라이딩해서 객체를 반환하게 하는 것을 의미합니다.
간단하게 설명하면
객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지를 서브 클래스에서 결정하는 것이다.
구성요소
- 제품(Product)
팩토리가 생성하는 객체를 나타내는 인터페이스나 추상 클래스입니다. 팩토리 패턴에서 생성되는 구체적인 객체들은 이 인터페이스를 구현하거나 해당 추상 클래스를 상속받습니다. - 콘크리트 제품(Concrete Product)
Product 인터페이스나 추상 클래스를 실제로 구현한 클래스들입니다. 팩토리에 의해 생성되어 클라이언트에 제공됩니다. - 팩토리(Factory)
객체를 생성하는 인터페이스나 추상 클래스로, 팩토리 메서드를 제공합니다. 팩토리는 클라이언트에게 객체를 생성하는 메서드를 제공하며, 이를 통해 객체 생성의 책임을 캡슐화합니다. - 콘크리트 팩토리(Concrete Factory)
Factory 인터페이스나 추상 클래스를 실제로 구현한 클래스로, 객체의 생성을 담당합니다. 각각의 콘크리트 팩토리는 특정 종류의 제품을 생성합니다. - 클라이언트(Client)
팩토리를 사용하여 객체를 생성하는 주체입니다. 클라이언트는 팩토리를 통해 제품을 생성하고, 생성된 제품을 사용합니다. 클라이언트는 구체적인 제품 클래스에 의존하지 않고, 팩토리를 통해 객체를 생성합니다.
public abstract class Animal {
// 추상 팩토리 메서드
abstract AnimalToy getToy();
}
abstract class AnimalToy {
abstract void identify();
}
class Dog extends Animal {
// 추상 팩토리 메서드 오버라이딩
@Override
AnimalToy getToy(){
return new DogToy();
}
}
class DogToy extends AnimalToy {
public void identify() {
System.out.println("DogToy");
}
}
class Cat extends Animal {
// 추상 팩토리 메서드 오버라이딩
@Override
AnimalToy getToy() {
return new CatToy();
}
}
class CatToy extends AnimalToy {
public void identify() {
System.out.println("CatToy");
}
}
package DesignPattern.factoryMethodPattern;
public class Driver {
public static void main(String[] args) {
Animal dog = new Dog();
Animal cat = new Cat();
AnimalToy dogToy = dog.getToy();
AnimalToy catToy = cat.getToy();
dogToy.identify();
catToy.identify();
}
}
위를 코드를 살펴보면
1. Animal 클래스는 추상 팩터리 메소드인 getToy()를 포함하고 있는 추상클래스 입니다.
2. AnimalToy 클래스는 Animal 클래스에서 생성되는 동물의 장난감을 나타내는 추상 클래스로, Factory 메서드가 생성할 객체의 상위 클래스(추상 클래스)이며 identify 메서드를 정의합니다.
3. Dog클래스와 Cat클래스는 추상 팩터리 메소드를 오버라이팅하는
Dog와 Cat에 맞게 각각의 toy를 생성하는 구체적인 구현 클래스입니다.
4. DogToy와 CatToy 클래스는 AnimalToy클래스의 구현 클래스로 팩토리 메소드가 생성할 객체를 의미합니다.
장단점
장점: 구체적인 클래스를 사용자로부터 분리가능, 제품군 쉽게 대체 가능
단점: 새로운 종류의 제품, 객체 제공 어려움
오버라이드된 메서드가 객체를 반환하는 패턴
7. 전략 패턴
정책 패턴이라고도하며(strategy, policy pattern)
객체의 행위를 바꾸고싶은 경우 직접 수정하지 않고 전략이라고 하는 '캡슐화한 알고리즘'을 컨텍스트 안에서 바꾸는 패턴입니다.
전략 패턴의 구성요소
1. 전략 메서드를 가진 전략 객체
2. 전략 객체를 사용하는 컨텍스트(전략 객체의 사용자/ 소비자)
3.전략 객체를 생성해 컨텍스트에 주입하는 클라이언트(제 3자, 전략 객체의 공급자)
위처럼 클라이언트는 다양한 전략 중 하나를 선택해 생성한 후에 컨텍스트에 주입합니다.
먼저 보급 장교가 군인에게 사용할 무기를 주고 그 무기에 따라 전투를 수행하는 상황이 있습니다.
이때 이 상황을 전략 패턴에 따라 생각해보면, 무기는 전략/ 군인은 컨텍스트/ 보급장교는 제 3자 클라이언트가 됩니다.
아래의 코드를 통해 이해해 보겠습니다.
// 다양한 전략인 무기의 추상 클래스
package DesignPattern.strategyPattern;
public interface Strategy {
void runStrategy();
}
package DesignPattern.strategyPattern;
public class StrategyGun implements Strategy {
@Override
public void runStrategy() {
// TODO Auto-generated method stub
System.out.println("탕! 타탕! 탕탕!");
}
package DesignPattern.strategyPattern;
public class StrategySword implements Strategy {
@Override
public void runStrategy() {
System.out.println("쳉.. 채쟁챙 챙챙");
}
}
package DesignPattern.strategyPattern;
public class StrategyBow implements Strategy{
@Override
public void runStrategy() {
System.out.println("슝..쐐액..쉑, 최종 병기");
}
}
총, 검, 활을 사용하는 전략을 구현합니다.
이번에는 무기(전략)을 사용할 군인(컨텍스트)을 구현합니다.
package DesignPattern.strategyPattern;
public class Soldier {
void runContext(Strategy strategy) {
System.out.println("전투 시작");
strategy.runStrategy();
System.out.println("전투 종료");
}
}
마지막으로 무기(전략)를조달(생성)해서 군인(컨텍스트)에게 지급(주입)해 줄 보급 장교(클라이언트, 제3자)를 구현합니다
package DesignPattern.strategyPattern;
public class Client {
public static void main(String[] args) {
Strategy strategy = null;
Soldier rambo = new Soldier();
// 총을 람보에게 전달해서 전투를 수행하게 한다.
strategy = new StrategyGun();
rambo.runContext(strategy);
System.out.println();
// 검을 람보에게 전달해서 전투를 수행하게 한다.
strategy = new StrategySword();
rambo.runContext(strategy);
System.out.println();
// 화을 람보에게 전달해서 전투를 수행하게 한다.
strategy = new StrategyBow();
rambo.runContext(strategy);
}
}
위의 코드에서처럼 전략 패턴은 다양하게 전략을 변경하며 컨텍스트를 실행할 수 있습니다. 템플릿 패턴과 유사하며, 단일상속만 있는 템플릿메서드보다는 전략 패턴을 더 많이 활용합니다.
장단점
장점: 재사용이 쉬워 코드 중복이 적음, 확장과 유지보수 용이
단점: 사용자는 각전략이 어떻게 동작하는지 알고 명시적으로 전략을 선택해야함, 클라이언트가 전략 변경에 대한 책임이 있음, 필요하지 않은 정보를 전략 구현체가 가지고 있는 경우가 발생(인터페이스가 아닌 추상 클래스로 필요한 메서드를 구현하거나, 인터페이스분리 윈칙, 최소한의 메서드 구현등으로 해결가능)
클라이언트가 전략을 생성해 전략을 실행할 컨텍스트에 주입하는 패턴
8. 템플릿 콜백 패턴
템플릿 콜백 패턴은 전략패턴의 변형으로, 스프링의 DI에서 사용하는 특별한 전략 패턴입니다.
스프링의 JDBC Template , Rest Template,Redis Template 등에서 사용됩니다.
전략 패턴과 모두 동일하지만, 전략을 익명 내부 클래스로 정의해서 사용하는 특징이 있습니다.
기존의 전략 패턴은 컴파일 타임에서 클래스로 만든뒤 구현체를 주입하는 형식이지만
템플릿 콜백 패턴은 런타임에서 익명 클래스를 이용해 동적으로 전략 알고리즘을 주입하는 것 입니다.
전략 패턴에서 정의했던 클래스들을 Client나 Context에 포함시켜 버린 것
콜백(Callback)이란?
프로그래밍에서 콜백은 하나의 오브젝트를 다른 오브젝트의 메소드에 매개변수로 넘겨주는 실행 가능한 코드를 말합니다.
특정 로직을 담은 일종의 함수를 넘겨서 실행시키기 위해 상용됩니다. 자바8에서는 람다를 통해 콜백이 가능합니다.
템플릿 콜백 구성요소
- 템플릿 메서드 (Template Method)
알고리즘의 구조를 정의하는 메서드로, 일련의 단계 중 일부를 추상 메서드 또는 구체 메서드로 구현합니다. - 콜백 (Callback)
템플릿 메서드에서 호출되는 구체 메서드를 가지고 있는 인터페이스 또는 추상 클래스입니다. 콜백은 알고리즘의 일부를 구현하는데 필요한 메서드를 정의합니다.
package exTempleteCallBack;
// 위와 동일
public interface Strategy {
public abstract void ChoosePen();
}
package exTemplateCallBack_Refactoring;
public class Student {
public void takeNotes(String Pen) {
System.out.println("=== 선생님께서 펜을 주십니다. ===");
// 변경된 부분
takePen(Pen).ChoosePen();
System.out.println("필기를 시작합니다.");
}
// Strategy를 익명 클래스로 반환하는 메서드
private Strategy takePen(String Pen) {
// 익명 클래스 사용
return new Strategy() {
@Override
public void ChoosePen() {
System.out.println(Pen + "을 잡았습니다.");
}
};
}
}
package exTemplateCallBack_Refactoring;
public class Teacher {
public static void main(String[] args) {
Student student = new Student();
student.takeNotes("검정펜");
student.takeNotes("빨간펜");
student.takeNotes("파란펜");
student.takeNotes("무지개 형관펜");
}
}
위의 예제에서 Student안에 takeNote라는 기본 템플릿이 있고,
takePen안에 익명 클래스를 만들어 전략을 반환합니다.
전략을 익명 내부 클래스로 구현한 전략 패턴
9. 파사드 패턴
파사드 패턴은 여러 개의 서브 시스템을 감싸고 있는 외부의 단일 인터페이스를 제공하여 클라이언트가 각 서브 시스템을 더 쉽게 사용할 수 있도록 하는 패턴입니다.
여러 개의 저수준 인터페이스를 일일이 호출하지 않고 파사드 패턴을 이용해 고수준 인터페이시의 호출 한번만으로 동작을 가능하게 해줍니다.
또한 메인시스템과 서브 시스템의 중간에 위치하여, 새로운 인터페이스 계층을 추가하며 시스템간 의존성을 해결합니다.
저수준의 인터페이스를 변경할때 통합 인터페이스의 클라이언트의 코드만 변경하므로 저수준 인터페이스의 의존성이 느슨해집니다.
파사드 패턴 구성요소
- 파사드(Facade)
클라이언트와 각 서브시스템 간의 중간자 역할을 하는 클래스입니다. 주로 단일 메서드로 간략하게 구성되며, 이를 통해 복잡한 서브시스템을 단순화합니다. - 서브시스템(Subsystem)
전체 시스템의 일부로서, 각각 독립적인 기능을 수행하는 클래스 또는 그룹입니다. - 클라이언트(Client)
파사드를 사용하여 서브시스템과 상호 작용하는 클래스입니다. 클라이언트는 파사드를 통해 간단한 인터페이스를 이용하여 서브시스템을 조작할 수 있습니다.
// 파사드 클래스
public class ComputerFacade {
private CPU cpu;
private Memory memory;
private HardDrive hardDrive;
public ComputerFacade() {
this.cpu = new CPU();
this.memory = new Memory();
this.hardDrive = new HardDrive();
}
// 복잡한 부팅 프로세스를 단순화한 메서드
public void startComputer() {
cpu.freeze();
memory.load(BOOT_ADDRESS, hardDrive.read(BOOT_SECTOR, SECTOR_SIZE));
cpu.jump(START_ADDRESS);
cpu.execute();
}
}
// 서브시스템 클래스들
class CPU {
public void freeze() { /* ... */ }
public void jump(long position) { /* ... */ }
public void execute() { /* ... */ }
}
class Memory {
public void load(long position, byte[] data) { /* ... */ }
}
class HardDrive {
public byte[] read(long lba, int size) { /* ... */ }
}
// 클라이언트
public class Client {
public static void main(String[] args) {
ComputerFacade computer = new ComputerFacade();
computer.startComputer();
}
}
위의 코드를 보면 ComputerFacade에서 startComputer라는 하나의 메소드를 통해 start를 위해 필요한 클래스의 메서드들을 호출하고 있습니다. 이렇게 하면 Client는 단일 메서드 호출만으로 복잡한 부팅 프로세스를 수행하고 내부동작에 대해 알 필요가 없습니다.
최소 지식 윈칙
파사드 패턴은 최소 지식만을 적용해 객체의 상호작용을 설정하여 유지보수를 용이하게 합니다.
- 자기 자신만의 객체 사용
- 메서드에 전달된 매개변수 사용
- 메서드에서 생성된 객체 사용
- 객체에 속하는 메서드 사용
https://hirlawldo.tistory.com/172
[Java][디자인 패턴] 11. 파사드 패턴 (Facade Pattern)
디자인패턴 [Java][디자인 패턴] 11. 파사드 패턴 (Facade Pattern) 파사드는 요즘과 같이 협업과 대형 시스템을 개발하고 배포하는 데 자주 응용되는 패턴이다. (API 등) 파사드 패턴은 강력한 결합 구조
hirlawldo.tistory.com
'JAVA' 카테고리의 다른 글
[JAVA]자바와 객체지향(5) - 스프링 삼각형과 설정정보 (2) | 2024.03.12 |
---|---|
[JAVA]자바와 객체지향(3) - 자바가 확장한 객체지향 (0) | 2024.02.27 |
[JAVA] 자바와 객체지향(2) - 객체 지향 4대 특성 알아보기 (1) | 2024.02.20 |
[JAVA]자바와 객체지향(1) - JAVA가 만들어진 이유와 실행환경 간단히 살펴보기 (0) | 2024.02.13 |
[JAVA]객체 지향 프로그래밍의 5가지 원칙 (SOLID 원칙) 예시와 개념 (2) | 2024.02.05 |