어댑터 패턴/ 파사드 패턴

존스노우·2024년 5월 22일
0

디자인패턴

목록 보기
5/6

어댑터 패턴 예시 코드





  • 각 인터페이스 별로 구현체 구현
  • 예시상황 duck객체가 모자라서 turkey객체를 사용해야된다
  • 바로 사용을 할 수 없으니 어댑터를 만들어서 해결한다.

어댑터 패턴의 정의


  • 다른 인터페이스로 변환!
  • 새로 바뀐 인터페이스로 감쌀때 컴포지션을 이용함 위에 코드 처럼.

  • 뒤에나오는 이너뮬 레이터도 똑같은 어댑터 코드라 생략
  • 요런식으로.

퍼사드 패턴


  • 쓰기 쉬운 인터페이스 제공
  • 파사드는 서브시스템 클래스를 캡슐화해 클라이언트와 서브시스템 간의 결합도를 낮추기 때문에 서브시스템의 변경이 클라이언트에 미치는 영향을 최소화할 수 있다
  • 파사드 패턴은 복잡한 서브시스템을 단순화된 인터페이스로 제공하여 클라이언트가 쉽게 사용할 수 있도록 함

정의

  • 서브시스템에 있는 일련의 인터페이스를 통합 인터페이스로 묶어 줌.
  • 고수준 인터페이스도 정의함으로 서브시스템 편리하게 사용 가능함

추가 공부

어댑터 패턴은 언제쓰지?

  • 레거시 시스템 통합
    • 기존의 레거시 시스템을 새로운 시스템에 통합해야 할 때, 레거시 시스템의 인터페이스를 새로운 시스템에서 요구하는 인터페이스로 변환하는 데 어댑터 패턴을 사용
  • 외부 라이브러리 또는 API 사용
    • 외부에서 제공하는 라이브러리나 API를 사용할 때, 해당 라이브러리나 API의 인터페이스가 애플리케이션에서 필요한 인터페이스와 다를 수 있습니다.

예시

// 추상화된 결제 인터페이스
public interface PaymentGateway {
    boolean processPayment(String paymentId, double amount);
    void refundPayment(String paymentId, double amount);
}

// 결제 시스템 A의 어댑터
public class PaymentGatewayAAdapter implements PaymentGateway {
    private PaymentGatewayA paymentGatewayA;

    public PaymentGatewayAAdapter(PaymentGatewayA paymentGatewayA) {
        this.paymentGatewayA = paymentGatewayA;
    }

    @Override
    public boolean processPayment(String paymentId, double amount) {
        // PaymentGatewayA의 API를 호출하여 결제 처리
        return paymentGatewayA.pay(paymentId, amount);
    }

    @Override
    public void refundPayment(String paymentId, double amount) {
        // PaymentGatewayA의 API를 호출하여 환불 처리
        paymentGatewayA.refund(paymentId, amount);
    }
}

// 결제 시스템 B의 어댑터
public class PaymentGatewayBAdapter implements PaymentGateway {
    private PaymentGatewayB paymentGatewayB;

    public PaymentGatewayBAdapter(PaymentGatewayB paymentGatewayB) {
        this.paymentGatewayB = paymentGatewayB;
    }

    @Override
    public boolean processPayment(String paymentId, double amount) {
        // PaymentGatewayB의 API를 호출하여 결제 처리
        return paymentGatewayB.processTransaction(paymentId, amount);
    }

    @Override
    public void refundPayment(String paymentId, double amount) {
        // PaymentGatewayB의 API를 호출하여 환불 처리
        paymentGatewayB.refundTransaction(paymentId, amount);
    }
}

// 애플리케이션 코드
public class PaymentService {
    private PaymentGateway paymentGateway;

    public PaymentService(PaymentGateway paymentGateway) {
        this.paymentGateway = paymentGateway;
    }

    public boolean processPayment(String paymentId, double amount) {
        return paymentGateway.processPayment(paymentId, amount);
    }

    public void refundPayment(String paymentId, double amount) {
        paymentGateway.refundPayment(paymentId, amount);
    }
}
  • 어댑터패턴은결국 한개의 타깃 인터페이스를두고 그걸 그냥 내시스템에 맞게 구현하는거네?

파사드 패턴은 언제 쓰일까

// 주문 처리를 위한 복잡한 서브 시스템
public class OrderProcessor {
    public void validateOrder(Order order) {
        // ...
    }

    public void processPayment(Order order) {
        // ...
    }

    public void updateInventory(Order order) {
        // ...
    }

    public void shipOrder(Order order) {
        // ...
    }
}

// 파사드 클래스
public class OrderFacade {
    private final OrderProcessor orderProcessor;

    public OrderFacade(OrderProcessor orderProcessor) {
        this.orderProcessor = orderProcessor;
    }

    public void placeOrder(Order order) {
        orderProcessor.validateOrder(order);
        orderProcessor.processPayment(order);
        orderProcessor.updateInventory(order);
        orderProcessor.shipOrder(order);
    }
}

// 클라이언트 코드
public class Client {
    public static void main(String[] args) {
        OrderProcessor orderProcessor = new OrderProcessor();
        OrderFacade orderFacade = new OrderFacade(orderProcessor);

        Order order = new Order();
        // ... 주문 정보 설정

        orderFacade.placeOrder(order);
    }
}
// 은행 시스템의 복잡한 서브 시스템
public class AccountSystem {
    public void createAccount(String accountNumber) {
        // ...
    }

    public void closeAccount(String accountNumber) {
        // ...
    }
}

public class PaymentSystem {
    public void processPayment(String accountNumber, double amount) {
        // ...
    }
}

public class LoanSystem {
    public void applyForLoan(String accountNumber, double amount) {
        // ...
    }
}

// 파사드 클래스
public class BankingServiceFacade {
    private final AccountSystem accountSystem;
    private final PaymentSystem paymentSystem;
    private final LoanSystem loanSystem;

    public BankingServiceFacade(AccountSystem accountSystem, PaymentSystem paymentSystem, LoanSystem loanSystem) {
        this.accountSystem = accountSystem;
        this.paymentSystem = paymentSystem;
        this.loanSystem = loanSystem;
    }

    public void createAccount(String accountNumber) {
        accountSystem.createAccount(accountNumber);
    }

    public void closeAccount(String accountNumber) {
        accountSystem.closeAccount(accountNumber);
    }

    public void processPayment(String accountNumber, double amount) {
        paymentSystem.processPayment(accountNumber, amount);
    }

    public void applyForLoan(String accountNumber, double amount) {
        loanSystem.applyForLoan(accountNumber, amount);
    }
}

// 클라이언트 코드
public class Client {
    public static void main(String[] args) {
        AccountSystem accountSystem = new AccountSystem();
        PaymentSystem paymentSystem = new PaymentSystem();
        LoanSystem loanSystem = new LoanSystem();

        BankingServiceFacade bankingServiceFacade = new BankingServiceFacade(accountSystem, paymentSystem, loanSystem);

        String accountNumber = "1234567890";
        bankingServiceFacade.createAccount(accountNumber);
        bankingServiceFacade.processPayment(accountNumber, 1000.0);
        bankingServiceFacade.applyForLoan(accountNumber, 10000.0);
        bankingServiceFacade.closeAccount(accountNumber);
    }
}
  • 스프링에서 @Service 어노테이션을 사용하여 서비스 클래스를 정의하는 것은 파사드 패턴의 일종으로 볼 수 있다

  • 보통 실무에서 해당 도메인뿐만아니라 다른 도메인 서비스까지 불러서

  • 복합적으로 비즈니스로직을 처리하는데 이런게 다 퍼사드 패턴의 일종 아닐까?

// 도메인 모델
@Entity
public class User {
    // ...
}

@Entity
public class Order {
    // ...
}

// 도메인 서비스
@Service
public class UserService {
    // ...
}

@Service
public class OrderService {
    // ...
}

// 리포지토리
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    // ...
}

@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
    // ...
}

// 애플리케이션 서비스 (파사드)
@Service
public class UserOrderFacade {
    private final UserService userService;
    private final OrderService orderService;

    public UserOrderFacade(UserService userService, OrderService orderService) {
        this.userService = userService;
        this.orderService = orderService;
    }

    public void createUserAndOrder(User user, Order order) {
        userService.createUser(user);
        orderService.createOrder(order);
        // 추가 비즈니스 로직 처리
    }

    public List<Order> getOrdersByUser(User user) {
        // 사용자의 주문 목록 조회 로직
    }
}
  • DDD 와 파서드를 합친 모습.
  • 두개는 대치되는 개념이라생각했는데 그건아니었다
  • 지금 와서 생각나는건 요즘 DDD 로 도메인 별로 클래스를 나누는데.
  • 팀원들과의 소통을통해 만약 두 도메인을 이용해 로직을 처리할 일이 있을경우
  • 파사드 라는 이름을 붙여서 사용하는 규약같은게 있어야될거같다.
profile
어제의 나보다 한걸음 더

0개의 댓글