반응형
Facade Pattern
- 라이브러리(또는 서브시스템)에 대해 사용하기 편하기 간편한 인터페이스를 구성하기 위한 구조 패턴
- 라이브러리의 각 클래스와 메소드의 사용이 복잡하거나 바로 가져다 쓰기에는 어려울 때, 퍼사드 패턴으로 디테일은 내부로 묶고 사용자가 쓰기 쉽게 정리하는 것
- 퍼사드 (facade) 라는 뜻은 건물의 정면이라는 뜻
- 퍼사드 패턴이 내부는 숨기고 외관만 보여주는 것에 비유 (외관만 보고 전체를 이해)
- 퍼사드 패턴이 필요한 상황
- 라이브러리의 여러 API 를 조합해서 쓰는 상황에서 클라이언트의 코드의 라이브러리 의존성이 높을 때
- JAVA 로 email 을 보내려는 코드를 작성할 때 클라이언트는 email API 를 복잡하게 조합해야 함
public static void main(String[] args) { String from = "example@mail.com"; String to = "destination@mail.com"; String host = "127.0.0.1"; Properties properties = System.getProperties(); properties.setProperty("mail.smtp.host", host); Session session = Session.getDefaultInstance(properties); try { MimeMessage message = new MimeMessage(session); message.setFrom(new InternetAddress(from)); message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); message.setSubject("Test Mail"); message.setText("This is a test email."); Transport.send(message); } catch (MessagingException e) { e.printStackTrace(); } }
- 퍼사드 패턴의 구조
- 퍼사드 패턴의 예시
- 클라이언트는 퍼사드 (EmailSender) 만 가지고 작업
- 미리 정의된 API 를 쓰기 때문에 코드의 사용이 쉽고 간결해짐
- 복잡한 API 사용의 실수를 줄일 수 있음
public class EmailSender { private EmailSettings emailSettings; public EmailSender(EmailSettings emailSettings) { this.emailSettings = emailSettings; } public void sendEmail(EmailMessage emailMessage) { Properties properties = new Properties(); properties.put("mail.smtp.host", emailSettings.getHost()); Session session = Session.getDefaultInstance(properties); try { MimeMessage message = new MimeMessage(session); message.setFrom(new InternetAddress(emailMessage.getFrom())); message.addRecipient(Message.RecipientType.TO, new InternetAddress(emailMessage.getTo())); message.setSubject(emailMessage.getTitle()); message.setText(emailMessage.getContent()); Transport.send(message); } catch (MessagingException e) { e.printStackTrace(); } } } public static void main(String[] args) { EmailSettings emailSettings = new EmailSettings(); emailSettings.setHost("127.0.0.1"); EmailSender emailSender = new EmailSender(emailSettings); EmailMessage emailMessage = new EmailMessage(); emailMessage.setFrom("from@mail.com"); emailMessage.setTo("to@mail.com"); emailMessage.setTitle("email title"); emailMessage.setContent("email content"); emailSender.sendEmail(emailMessage); }
- 클라이언트는 퍼사드 (EmailSender) 만 가지고 작업
- 사용 시기
- 복잡한 서브 시스템에 대한 제한적이지만 간단한 인터페이스가 필요할 때
- 서브 시스템과의 결합도가 높아 의존성을 줄일 필요가 있을 때
- 장점
- 서브 시스템 간의 의존 관계가 많을 경우 이를 감소시키고 의존성을 한 곳으로 모을 수 있음
- 클라이언트가 퍼사드 클래스만 다루기 때문에 기능을 쉽게 이해하고 사용할 수 있음
- 단점
- 앱의 모든 클래스에 결합된 god object (client 와 모두 연관이있는 객체) 가 될 수 있음
- 코드가 추가되어서 유지보수 측면에서 관리 대상이 늘어남
Flyweight Pattern
- 메모리 사용량을 최소화하기 위해 재사용 가능한 객체를 공유할 수 있게 해주는 구조 패턴
- 캐시 (cache) 개념을 도입하여 패턴화 한 것
- 자주 변화하는 속성(extrinsit) 과 변하지 않는 속성(intrinsit) 으로 분리
- 변하지 않는 속성은 캐시함 (따로 저장하고 재사용해 메모리를 아낌)
- 플라이웨이트 패턴이 필요한 상황
- Editor 프로그램에서 character 를 표현하는 객체의 예시
- Nanum, 12 가 중복되는 경우 메모리 사용량이 늘어남
public class Character { private char value; private String color; private String fontFamily; private int fontSize; public Character(char value, String color, String fontFamily, int fontSize) { this.value = value; this.color = color; this.fontFamily = fontFamily; this.fontSize = fontSize; } } public class Client{ public static void main(String[] args){ Character c1 = new Character('h', "red", "Nanum", 12); Character c2 = new Character('e', "white", "Nanum", 12); Character c3 = new Character('l', "white", "Nanum", 12); Character c4 = new Character('l', "blue", "Nanum", 12); Character c5 = new Character('o', "white", "Nanum", 12); } }
- Nanum, 12 가 중복되는 경우 메모리 사용량이 늘어남
- Editor 프로그램에서 character 를 표현하는 객체의 예시
- 플라이웨이트 패턴의 구조 (simple version)
- 플라이웨이트 패턴 적용 예시
- 처음에만 만들고 나머지는 재사용한다.
public final class Font { final String family; // 글꼴 이름 final int size; // 글꼴 크기 public Font(String family, int size) { this.family = family; this.size = size; } public String getFamily() { return family; } public int getSize() { return size; } } public class Character { private char value; // 문자 private String color; // 색상 private Font font; // 공유 객체 (intrinsic) public Character(char value, String color, Font font) { this.value = value; this.color = color; this.font = font; } } public class FontFactory { private Map<String, Font> cache = new HashMap<>(); public Font getFont(String font) { if (cache.containsKey(font)) { // 캐시에 존재하면 반환 return cache.get(font); } else { // 새로 생성 후 캐시에 추가 String[] split = font.split(":"); // "family:size" 포맷 String family = split[0]; int size = Integer.parseInt(split[1]); Font newFont = new Font(family, size); cache.put(font, newFont); // 캐싱 return newFont; } } } public class Client { public static void main(String[] args) { FontFactory fontFactory = new FontFactory(); Character c1 = new Character('h', "white", fontFactory.getFont("nanum:12")); Character c2 = new Character('e', "white", fontFactory.getFont("nanum:12")); Character c3 = new Character('l', "white", fontFactory.getFont("nanum:12")); // 동일한 Font 객체를 재사용하여 메모리 절약 } }
- 처음에만 만들고 나머지는 재사용한다.
- 사용 시기
- 메모리에 오래 상주하는 객체가 많이 생성되어 메모리 사용이 높을 때
- 공통적인 인스턴스를 많이 생성하는 로직이 포함되는 경우
- 장점
- 메모리 사용량과 프로그램 속도를 개선할 수 있음
- 인스턴스화(new) 하면 데이터 생성 및 메모리 적재에 시간이 소모 됨
- 메모리 사용량과 프로그램 속도를 개선할 수 있음
- 단점
- 캐싱등을 처리 하기 위한 클래스 도입으로 코드의 복잡성이 올라감
Proxy Pattern
- 대상 원본 객체에 대한 접근을 제어하거나 대리할 수 있도록 해주는 구조 패턴(proxy = 대리인)
- 클라이언트가 대상 원본 객체를 직접 쓰는 것이 아니라 대리인을 거쳐 쓰는 개념
- Client 가 Service 모종의 이유로 수정을 못한다. 부가 기능을 둔다.
- 프록시는 한번만 감쌀 수 있다.
- 아래와 같은 이유로 수정을 하고 싶은데, 원본 객체를 수정할 수 없을 때, 원래 객체와 같은 인터페이스를 가지는 proxy 를 두어 처리
- 대상 클래스가 민감한 정보를 가지고 있어 권한에 따라 접근을 제한 하고 싶을 때
- 인스턴스화 하기 무거워 lazy 초기화를 하고 싶을 때 등등
- 프록시 패턴의 효과
- 보안
- 클라이언트 작업 권한에 따라 유효한 권한일 경우에만 전달
- 캐싱
- 데이터가 캐시에 아직 존재하지 않는 경우에만 작업이 실행되도록 할 수 있음
- 데이터 유효성 검사
- 입력을 원본 객체로 전달하기 전에 유효성을 미리 체크하게 할 수 있음
- 지연 초기화
- 원본 객체의 생성 비용이 비싼 경우 프록시가 필요할 때 생성하게 할 수 있음
- 로깅
- 메소드 호출과 상대 매개 변수에 대해 중간에 기록을 남길 수 있음
- 보안
- 프록시 패턴의 구조
- 프록시 패턴의 적용 예시
- 사용 시기
- 기능을 추가하고 싶은데, 기존의 특정 객체를 수정할 수 없는 상황 일 때
- 지연 초기화, 접근 제어, 로깅, 캐싱 등등
- 기능을 추가하고 싶은데, 기존의 특정 객체를 수정할 수 없는 상황 일 때
- 장점
- 원래 하는 기능을 유지하며 부가 기능을 원래 사용법과 같이 쓸 수 있음
- OCP 준수
- 기존 대상 객체 코드의 변경 없이 기능 확장
- SRP 준수
- 대상 객체는 자신 기능에 집중, 부가 기능은 프록시가 집중
- 단점
- 많은 프록시 클래스를 도입해야 하므로 코드의 복잡성이 증가
- 프록시 클래스 자체에 들어가는 자원이 많아 진다면 처리 비용 증가
반응형
'3학년 2학기 학사 > 소프트웨어 분석 및 설계' 카테고리의 다른 글
[소프트웨어 분석 및 설계] L22. State Pattern, Strategy Pattern (1) | 2024.12.13 |
---|---|
[소프트웨어 분석 및 설계] L21. Interpreter Pattern, Iterator Pattern (1) | 2024.12.13 |
[소프트웨어 분석 및 설계] L18. Composite Pattern, Decorator Pattern (0) | 2024.12.13 |
[소프트웨어 분석 및 설계] L17. Adapter Pattern, Bridge Pattern (3) | 2024.12.13 |
[소프트웨어 분석 및 설계] L16. Abstract Factory Pattern, Builder Pattern, Prototype Pattern (0) | 2024.12.13 |