디자인 패턴
실제 개발 현장에서 비즈니스 요구사항을 프로그래밍으로 처리하면서 만들어진 다양한 해결책 중에 베스트 프랙티스를 정리한 것이며 객체지향 특성과 설계 원칙을 기반으로 구현되어 있으며 객체 지향의 특성 중 상속, 인터페이스, 합성을 이용합니다.
어댑터 패턴
구조적 디자인 패턴 중 하나로, 한 인터페이스를 다른 인터페이스로 변환하기 위해 사용되며 이 패턴은 이미 존재하는 클래스나 인터페이스를 다른 클래스에서 사용할 때 유용합니다. 쉽게 이해해 보자면 휴대폰 충전기의 경우 휴대폰을 직접 전원콘센트에 연결할 수 없기 때문에 충전기가 핸드폰과 전원 콘센트 사이에서 둘을 연결해 주는 변환기의 역할을 수행해 줍니다.
구성 요소
Target 인터페이스
사용하려는 인터페이스를 정의 할 때는 Target 인터페이스를 사용합니다.
Adaptee 클래스
이미 존재하는 클래스 또는 인터페이스로 클라이언트가 사용하려는 인터페이스와 호환되지 않습니다.
Adapter 클래스
대상 인터페이스를 사용할 수 있도록 Adaptee 클래스를 대항 인터페이스로 변환해 주는 클래스입니다.
예제 코드
// 어댑터 패턴 적용 안된 코드
public class ServiceA {
void runServiceA() {
System.out.println("ServiceA");
}
}
public class ClientWithNoAdapeter {
public static void main(String[] args) {
ServiceA sa1 = new ServiceA();
sa1.runServiceA();
}
}
// 적용된 코드
public class AdapterServiceA {
ServiceA sa1 = new ServiceA();
void runService() {
sa1.runServiceA();
}
}
public class ClientWithAdapter {
public static void main(String[] args) {
AdapterServicA asa1 = new AdapterServiceA();
asa1.runService();
}
}
프록시 패턴
다른 객체에 대한 접근을 제어하거나 중개하는 데 사용됩니다(다른 누군가를 대신해서 역할을 수행한다고 생각하면 됩니다.). 주로 실제 객체에 대한 접근을 제한하거나 보안, 로깅, 지연로딩등과 같은 추가 기능을 제공하려고 할 때 쓰입니다.
구성요소
Subject 인터페이스
프록시 객체가 구현해야 하는 공통 인터페이스를 정의합니다.
RealSubject 클래스
실제로 수행해야 할 작업을 수행하는 클래스 또는 객체를 나타냅니다.
Proxy 클래스
클라이언트의 요청을 중개하고 실제 객체에 대한 접근을 제어합니다.
예제 코드
//proxy 적용 X
public class Servic {
public String runSomething() {
return "서비스 짱!!!!";
}
}
public class ClientWithNoProxy {
public static void main(String[] args){
// 프록시를 이용하지 않은 호출
Service service = new Service();
System.out.println(service.runSomething());
}
}
// Iservice interface 구현 + proxy 적용 O
public interface IService {
String runSomething();
}
public class Proxy implements IService{
public String runSomething() {
return "서비스 짱!!!";
}
}
public class ClientWithProxy {
public static void main(String[] args) {
IService proxy = new Proxy();
System.out.println(proxy.runSomething());
}
}
데코레이터 패턴
기존 객체의 구조를 수정하지 않고 동적으로 객체의 기능을 확장하거나 변경할 수 있게 해 줍니다. 이 패턴은 객체에 대한 기능을 계층적으로 추가하고 각 계층은 데코레이터 클래스로 나타내며, 클라이언트는 이러한 데코레이터를 조합하여 객체의 동작을 변경할 수 있습니다. 데코레이터와 프록시의 구현 방법이 같지만 프록시 패턴은 클라이언트가 최종적으로 돌려받는 반환값을 조작하지 않고 그대로 전달하지만 데코레이터 패턴은 클라이언트가 받는 반환값에 장식을 꾸며줍니다.
예시 코드
public class Decoreator implements IService {
IService service;
public String runSomething() {
System.out.println("호출에 대한 장식 주목적, 클라이언트에게 반환 결과에 장식을 더하여 전달");
service = new Service();
return "정말" + service.runSomething();
}
}
싱글턴 패턴
클래스가 단 하나의 인스턴스를 가지도록 보장하는 패터입니다. 이 패턴은 주로 전역적으로 공유해야 하는 객체나 공통된 자원을 효율적으로 관리하거나, 설정 객체, 로깅, 캐싱, 스레드풀, 데이터베이스 연결 등과 같은 리소스 관리에 사용됩니다.
예시 코드
public class Singleton {
private static Singleton instance;
// 생성자를 private로 선언하여 외부에서 직접 객체를 생성하는 것을 막음
private Singleton() {
// 생성자 코드
}
// 인스턴스를 반환하는 메서드
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
// 다른 메서드와 속성들
}
private로 선언되어 외부에서 직접 객체를 생성하는 것을 막으며 getInstance 메서드를 통해 객체를 반환하며 이 메서드는 처음 호출될 때에만 인스턴스를 생성하고 이후 호출에는 기존의 인스턴스를 반환합니다. 싱글턴 패턴은 멀티스레드 환경에서 주의해야 하며 동시에 여러 스레드에서 getInstance()를 호출할 때에도 하나의 인스턴스만 생성되도록 보장해야 합니다.
템플릿 메서드 패턴
알고리즘의 구조를 정의하며 일부 단계를 서브클래스로 미루는 패턴입니다
예시 코드
public class Dog{
public void playWithOwner(){
System.out.println("귀염둥이 이리온...");
System.out.println("멍! 멍!");
System.out.println("꼬리 살랑~");
System.out.println("잘했어");
}
}
// 상위 클래스 포함 Animal
public abstract class Animal {
// 템플릿 메서드
public void playWithOwner() {
System.out.println("귀염둥이 이리온...");
play();
runSomething();
System.out.println("잘했어");
}
// 추상 메서드
abstract void play();
// Hook(갈고리) 메서드
void runSomething() {
System.out.println("꼬리 살랑~");
}
}
// 하위 클래스 포함 Dog
Pulic class Dog extends Animal {
@Override
//추상 메서드 오버라이딩
void play() {
System.out.println("멍! 멍!");
}
@Override
// Hook 메서드 오버라이딩
void runSomething() {
System.out.println("멍! 멍! ~ 꼬리 살랑~");
}
}
전략 패턴
디자인 패턴의 꽃이라고 불리며 알고리즘, 동작 또는 행동을 정의하고 캡슐화하여 실행 중에 동적으로 선택하거나 교체할 수 있게 하는 패턴입니다. 전략패턴의 구성요소 세 가지인 전략 메서드를 가진 개체, 전략 객체를 사용하는 컨텍스트, 전략 객체를 생성해 컨테그트에 주입하는 클라이언트를 기억해야 합니다.
예시 코드
// 전략 인터페이스 정의
public interface PaymentStrategy {
void pay(int amount);
}
// 전략 인터페이스 클래스
public class CreditCardPayment implements PaymentStrategy {
private String cardNumber;
private String name;
public CreditCardPayment(String cardNumber, String name) {
this.cardNumber = cardNumber;
this.name = name;
}
@Override
public void pay(int amount) {
System.out.println("Paid $" + amount + " with credit card " + cardNumber);
}
}
public class PayPalPayment implements PaymentStrategy {
private String email;
public PayPalPayment(String email) {
this.email = email;
}
@Override
public void pay(int amount) {
System.out.println("Paid $" + amount + " with PayPal using email " + email);
}
}
// 전략 패턴 사용 컨텍스트 클래스
public class ShoppingCart {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void checkout(int amount) {
paymentStrategy.pay(amount);
}
}
* 참고 자료 : 자바 객체 지향의 원리와 이해
'Spring(Boot & FrameWork)' 카테고리의 다른 글
@Entity와 @Table의 차이 (0) | 2023.11.14 |
---|---|
ORM (0) | 2023.11.10 |
Entity 연관 관계 (0) | 2023.11.09 |