I am visible
algorithm
baekjoon
programmers
back-end
design-pattern
java
jpa
spring
bootcamp
lg-eureka
db
h2
devops
aws
docker
git
kubernetes
pipeline
front-end
css
html
js
os
linux
mac
window
record
diary
project
infinite-challenge
contact
recommended posts
home
>
back-end
> design-pattern
design-pattern
[design-pattern] singleton 패턴
intro : singleton 패턴에 대해서 알아보자 singleton 패턴이란 ? 싱글톤 패턴(Singleton Pattern)은 객체지향 프로그래밍에서 사용되는 생성 디자인 패턴 중 하나로, 클래스의 인스턴스가 오직 하나만 존재하도록 보장하는 패턴입니다. 이 패턴은 클래스가 단 하나의 인스턴스를 공유해야 할 때 유용하며, 주로 시스템에서 전역적으로 접근 가능한 유일한 객체가 필요할 때 사용된다. singleton 패턴은 언제 사용하는가 ? 싱글톤 패턴은 객체가 시스템 내에서 단 하나만 존재해야 할 때 사용합니다. 이 패턴은 전역적으로 공유되는 객체가 필요하거나, 여러 곳에서 동일한 인스턴스를 참조해야 할 때 유용하다. singleton 패턴 적용 코드 싱글톤으로 사용할 클래스 정의. 예제의 상황은 시스템에 전역적으로 적용될 테마색(light,dark)에 대한 설정을 진행할 클래스와 해당 클래스를 이용하여 테마 색이 적용되는 버튼, 텍스트필드, 라벨에 대한 상황을 보겠다. // 싱글톤으로 사용할 클래스 public class Theme { // 자기자신의 타입의 멤버변수를 선언한다. // static 키워드를 사용하는 이유는 전역적으로 하나만 사용하기 위함에 있음 private static Theme instance; // 테마 칼라를 선언한다. private String themeColor; // 생성자가 private인 이유는, 외부에서 해당 클래스 객체를 new 연산자로 // 생성하지 못하게 하기 위함에 있다. private Theme() { // 기본적으로 해당 객체가 생성되었을때는 테마 색은 light로 설정한다. this.themeColor = "light"; // Default theme } // 외부에서 객체를 생성하기 위해서는 해당 메서드를 이용해서 // 인스턴스를 반환받아야 한다. public static Theme getInstance() { // 만약 클래스의 instance 변수가 null 인경우 // 객체가 생성되어 있지 않다는 걸로 간주하고 new 연산자를 통해 내부에서 객체 생성 if (instance == null) { instance = new Theme(); } // 만약 instance 객체가 생성되어 있다면 주소값만 리턴 return instance; } // 테마 색을 얻는 메서드 public String getThemeColor() { return themeColor; } // 테마 색을 설정하는 메서드 public void setThemeColor(String themeColor) { this.themeColor = themeColor; } } 각 버튼과 텍스트필드 라벨에 대한 클래스를 정의하고 해당 클래스의 테마색은 싱글톤 클래스의 (Theme) 설정에 따라 display 메서드에서 themeColor가 다르게 출력된다. (색을 따로 dark로 변경하기 전까지는 light가 기본값) // 싱글톤 클래스에서 값을 참조할 클래스 public class Button { private String label; public Button(String label) { this.label = label; } // Theme.getInstance().getThemeColor(); 메서드가 실행 가능한 이유는 // getInstance() 메서드가 static 메서드이기 때문에 객체 생성없이도 호출이 가능하다. // 전역적으로 하나만 존재하기에 getThemeColor 메서드 호출값은 어디서 호출하던지 동일한 값을 보장함 public void display() { String themeColor = Theme.getInstance().getThemeColor(); System.out.println( "Button [" + label + "] displayed in " + themeColor + " theme." ); } } public class TextField { private String text; public TextField(String text) { this.text = text; } // 위 주석과 같은 맥락으로 static 메서드 이기 때문에 Theme.getInstance().getThemeColor(); 실행이 가능 public void display() { String themeColor = Theme.getInstance().getThemeColor(); System.out.println( "TextField [" + text + "] displayed in " + themeColor + " theme." ); } } public class Label { private String text; public Label(String text) { this.text = text; } // 위 주석과 같은 맥락으로 static 메서드 이기 때문에 Theme.getInstance().getThemeColor(); 실행이 가능 public void display() { String themeColor = Theme.getInstance().getThemeColor(); System.out.println( "Label [" + text + "] displayed in " + themeColor + " theme." ); } } 클라이언트 코드에서는 버튼, 텍스트필드, 라벨에 대한 객체를 생성하고 display 메서드를 호출한다. 간단하게 싱글톤 클래스에서 테마 색을 관리하고 있기에, Theme.getInstance().setThemeColor("dark"); 라인 이후에 다시 display 메서드를 호출하면 dark가 출력된다. 이와 같이 싱글톤 패턴으로 시스템 전역에 적용되어야 하는 설정값을 하나의 객체로 관리 할 수 있다. // Client code public class Main { public static void main(String[] args) { Button button = new Button("Submit"); TextField textField = new TextField("Enter your name"); Label label = new Label("Username"); // display 메서드는 내부적으로 테마 색을 출력하도록 되어있음 // 기본적으로 light가 기본값이기에, 이시점에서는 light로 출력됨 button.display(); textField.display(); label.display(); // 테마 색을 dark로 변경 Theme.getInstance().setThemeColor("dark"); // 아래 출력문은 테마색이 dark로 변경되어 출력됨 button.display(); textField.display(); label.display(); } } singleton 패턴 요약 싱글톤 패턴(Singleton Pattern)은 생성 패턴(Creational Pattern) 중 하나로, 특정 클래스의 인스턴스가 오직 하나만 존재하도록 보장하고, 이 인스턴스에 전역적으로 접근할 수 있게 해주는 디자인 패턴이다. 하지만 위 패턴을 실무에 적용할때는 조금 조심해서 적용해야하는게 멀티스레드 환경에서는 예상치 못한 동기화 문제를 발생시키는 경우도 있으니 조심해서 적용하도록 하자.
back-end
· 2024-11-29
[design-pattern] template method 패턴
intro : template method 패턴에 대해서 알아보자 Template Method 패턴이란 ? Template Method 패턴은 객체지향 설계에서 자주 사용하는 행동(Behavioral) 디자인 패턴 중 하나로, 상위 클래스에서 알고리즘의 구조를 정의하고, 하위 클래스에서 일부 세부적인 동작을 구체적으로 구현하도록 허용하는 패턴이다. 즉, 알고리즘의 골격을 정의하고 세부적인 내용은 서브클래스에서 완성되도록 유도한다. Template Method 패턴은 언제 사용하는가 ? Template Method 패턴은 알고리즘의 전체 구조(골격)는 고정하되, 특정 단계만 커스터마이징해야 할 때 사용됩니다. 이 패턴은 공통된 프로세스를 상위 클래스에 정의하고, 세부적인 동작을 하위 클래스에서 구현하도록 한다. Template Method 패턴 적용 코드 (1) 골격을 정의할 Beverage 추상 클래스, 차후 Beverage를 구현할 하위 클래스가 세부 역할을 정의하게 된다. abstract class Beverage { // Template method (큰 틀과 골격, 이부분은 변하지 않는다.) // 하위 클래스에서 재정의가 되지 않도록 final 키워드를 사용하였다. final void prepareRecipe() { boilWater(); brew(); pourInCup(); addCondiments(); } void boilWater() { System.out.println("Boiling water"); } void pourInCup() { System.out.println("Pouring into cup"); } // 하위 클래스에서 정의할 메소드들 abstract void brew(); abstract void addCondiments(); } Beverage 추상 클래스를 상속받아 구현한 Tea, Coffee 클래스이다. 해당 클래스들은 brew, addCondiments 메서드들을 각각 구현한다. // 실제 구현체 클래스에서 추상 메서드 재정의 class Tea extends Beverage { void brew() { System.out.println("Steeping the tea"); } void addCondiments() { System.out.println("Adding lemon"); } } // 실제 구현체 클래스에서 추상 메서드 재정의 class Coffee extends Beverage { void brew() { System.out.println("Dripping coffee through filter"); } void addCondiments() { System.out.println("Adding sugar and milk"); } } 클라이언트는 다음과 같이 Beverage 타입으로 Tea, Coffee 객체를 생성한다. (다형성의 특징) Beverage 클래스에 미리 정의된 prepareRecipe 메서드는 내부적으로 고유한 골격과 순서를 가지고 잇기 때문에 어떠한 구현체 클래스가 prepareRecipe 메서드를 호출하더라도 메서드 내부의 순서는 다르지 않고, brew, addCondiments 메서드의 Overriding 내용만 다르게 작동한다. // Client code public class Main { public static void main(String[] args) { Beverage tea = new Tea(); Beverage coffee = new Coffee(); System.out.println("\nMaking tea..."); tea.prepareRecipe(); System.out.println("\nMaking coffee..."); coffee.prepareRecipe(); } } Template Method 패턴 적용 코드 (2) 위 내용과 비슷한 상황이지만, Template Method가 분기에 따라 다르게 동작하는 상황에 대해서도 적용할 수 있다. (보다 실무에 가까운 예제) // 해당 패턴이 인터페이스를 사용하지 않는 이유는 // 템플릿 메서드는 상위 클래스에서 구현이 되어 있어야 하기 때문. // 추상클래스의 사용 이유가 된다. abstract class DataProcessor { // Template method (큰 틀과 골격, 이부분은 변하지 않는다.) // 하위 클래스에서 재정의가 되지 않도록 final 키워드를 사용하였다. public final void process(String data) { loadData(data); if (isValidData(data)) { processData(data); saveData(data); } else { System.out.println("Data is invalid, processing aborted."); } } // 추상 메서드를 정의하고 하위 클래스에서 구현할수 있도록 정의 // protected 키워드는 자식 클래스에서는 접근가능한 접근 제어자 protected abstract void loadData(String data); protected abstract boolean isValidData(String data); protected abstract void processData(String data); protected abstract void saveData(String data); } BeveraDataProcessorge 추상 클래스를 상속받아 구현한 CSVDataProcessor, JSONDataProcessor 클래스이다. 해당 클래스들은 loadData, isValidData, processData, saveData 메서드들을 각각 구현한다. 위 예제와 같은 역할이다 결국 상위 클래스에서 정의한 추상 메서드들을 각 클래스의 역할에 맞게 재정의 할 뿐이다. // 실제 구현체 클래스에서 추상 메서드 재정의 class CSVDataProcessor extends DataProcessor { @Override protected void loadData(String data) { System.out.println("Loading data from CSV file: " + data); } @Override protected boolean isValidData(String data) { return data != null && data.contains("CSV"); } @Override protected void processData(String data) { System.out.println("Processing CSV data"); } @Override protected void saveData(String data) { System.out.println("Saving CSV data to database"); } } // 실제 구현체 클래스에서 추상 메서드 재정의 class JSONDataProcessor extends DataProcessor { @Override protected void loadData(String data) { System.out.println("Loading data from JSON file: " + data); } @Override protected boolean isValidData(String data) { return data != null; } @Override protected void processData(String data) { System.out.println("Processing JSON data"); } @Override protected void saveData(String data) { System.out.println("Saving JSON data to database"); } } 클라이언트 코드 즉 Main에서 DataProcessor 타입으로 CSVDataProcessor, JSONDataProcessor 객체들을 사용한다. 이때 실행하는 메서드 process 는 추상클래스인 DataProcessor의 템플릿 메서드 이므로, 상위클래스에서 정의된 순서대로 실행된다. (템플릿 메서드 안에 내부적으로 메서드가 순차적으로 정의 되어 있음) // Client code public class Main { public static void main(String[] args) { DataProcessor csvProcessor = new CSVDataProcessor(); csvProcessor.process("CSV data"); System.out.println(); DataProcessor jsonProcessor = new JSONDataProcessor(); jsonProcessor.process("XML data"); } } Template Method 패턴 요약 Template Method 패턴은 알고리즘의 골격(공통된 흐름)을 상위 클래스에서 정의하고, 세부적인 구현은 하위 클래스에서 담당하게 하는 행동(Behavioral) 디자인 패턴이다.
back-end
· 2024-11-26
[design-pattern] strategy 패턴
intro : strategy 패턴에 대해서 알아보자 Strategy 패턴이란 ? Strategy 패턴은 특정 작업을 수행하는 여러 방식(즉, 전략)을 정의하고, 필요에 따라 이를 교체하며 사용할 수 있도록 설계된 행동 패턴입니다. 쉽게 말해, 어떤 작업을 처리하는 방식(모드)을 각각의 클래스로 분리하고, 실행 시점에 적절한 전략을 선택해 사용하는 것입니다. 예를 들어, 다이소에서 물건을 결제할 때 다양한 결제 방식을 선택할 수 있는 화면이 나온다고 가정해 보겠습니다. 사용할 수 있는 결제 방식으로는 카드 결제, 현금 결제, 카카오페이 결제, 애플페이 결제, 네이버페이 결제 등이 있을 수 있습니다. 만약 이러한 결제 프로그램을 우리가 작성할 때, 결제라는 행위는 동일하지만, 선택된 결제 방식에 따라 수행되어야 하는 로직이 달라지게 됩니다. 이처럼 다양한 결제 방식을 유연하게 처리하기 위해 Strategy 패턴을 적용할 수 있습니다. Strategy 패턴을 통해 각 결제 방식을 별도 클래스로 분리하고, 실행 시점에 적절한 전략을 선택하여 사용함으로써 코드의 확장성과 유연성을 높일 수 있습니다. Strategy 패턴은 언제 사용하는가 ? Strategy 패턴은 행동(Behavior)을 캡슐화하고, 다양한 알고리즘(전략)을 동적으로 교체할 수 있도록 설계할 때 사용한다. 이 패턴은 특정 작업을 수행하는 여러 알고리즘(전략)들이 존재하며, 이들 사이를 유연하게 전환하거나 교체해야 할 때 유용하다. Strategy 패턴 적용 코드 (1) 결제 전략에 사용할 인터페이스 // 결제 방식에 대한 인터페이스 interface PaymentStrategy { void pay(int amount); } 결제 전략 인터페이스를 구현한 실제 전략 클래스 (구현체) // PaymentStrategy 인터페이스를 구현한 신용카드 결제 방식 클래스 구성 class CreditCardPayment implements PaymentStrategy { private String name; private String cardNumber; public CreditCardPayment(String name, String cardNumber) { this.name = name; this.cardNumber = cardNumber; } @Override public void pay(int amount) { System.out.println(amount + " paid with credit card"); } } // PaymentStrategy 인터페이스를 구현한 페이팔 결제 방식 클래스 구성 class PayPalPayment implements PaymentStrategy { private String email; public PayPalPayment(String email) { this.email = email; } @Override public void pay(int amount) { System.out.println(amount + " paid using PayPal"); } } 각 전략(신용카드결제, 페이팔결제) 에 맞게 결제 메소드를 호출할수 있는 클래스 // CreditCardPayment, PayPalPayment 클래스를 통해 객체를 생성할 수 있는 ShoppingCart 클래스 // setPaymentStrategy 메소드를 통해 어떤 전략이던지 동적으로 선택할 수 있다. // 해당 클래스의 checkout 메소드를 통해 각 전략에 맞는 결제 메소드를 실행 할 수 있다. class ShoppingCart { private PaymentStrategy paymentStrategy; public void setPaymentStrategy(PaymentStrategy paymentStrategy) { this.paymentStrategy = paymentStrategy; } public void checkout(int amount) { paymentStrategy.pay(amount); } } 클라이언트 코드 에서 ShoppingCart 클래스 setPaymentStrategy 메소드를 통해 신용카드 결제 방식으로 결제, 두번째로는 setPaymentStrategy 메소드를 통해 페이팔 결제방식 으로 전환. 다음과 같은 코드를 통해 각 상황에 맞게 전략을 동적으로 선택 할 수 있음을 알 수 있다. // Client code public class Main { public static void main(String[] args) { ShoppingCart cart = new ShoppingCart(); cart.setPaymentStrategy( new CreditCardPayment( "John Doe", "1234567890123456" ) ); //100 paid with credit card cart.checkout(100); cart.setPaymentStrategy( new PayPalPayment( "johndoe@example.com" ) ); // 200 paid using PayPal cart.checkout(200); } } Strategy 패턴 적용 코드 (2) 문자열을 압축하는 프로그램을 구성할건데, 압축하는 방식을 두가지 전략으로 구성하고자 한다. 1번은 각 문자가 연속되는 횟수를 기록하여 문자열을 압축하는 방식, 2번은 문자열의 각 모음을 정해진 숫자로 변환하는 방식이 있다. 해당 상황을 가정하고 아래와 같은 코드를 따라가보자. 압축 전략에 사용할 인터페이스 interface CompressionStrategy { String compress(String data); } 압축 전략 인터페이스를 구현한 실제 전략 클래스 (구현체) class RunLengthEncoding implements CompressionStrategy { @Override public String compress(String data) { StringBuilder compressed = new StringBuilder(); int count = 1; for (int i = 1; i <= data.length(); i++) { if (i < data.length() && data.charAt(i) == data.charAt(i - 1)) { count++; } else { compressed.append(data.charAt(i - 1)); compressed.append(count); count = 1; } } return compressed.toString(); } } class SimpleReplacementCompression implements CompressionStrategy { @Override public String compress(String data) { return data.replace("a", "1") .replace("e", "2") .replace("i", "3") .replace("o", "4") .replace("u", "5"); } } 각 전략(RunLengthEncoding, SimpleReplacementCompression) 에 맞게 압축 메소드를 호출할 수 있는 클래스 // Context class class Compressor { private CompressionStrategy strategy; public void setCompressionStrategy(CompressionStrategy strategy) { this.strategy = strategy; } public String compress(String data) { return strategy.compress(data); } } 클라이언트 코드 에서 Compressor 클래스 setCompressionStrategy 메소드를 통해 RunLengthEncoding방식으로 압축, 두번째로는 setCompressionStrategy메소드를 통해 SimpleReplacementCompression 방식으로 전환. 결국 1번에서 확인했던 전략 패턴의 큰 틀의 구조와 같다고 볼 수 있다. 전략패턴을 적용할 상황만 살짝 다를 뿐이었다. // Client code public class Main { public static void main(String[] args) { Compressor compressor = new Compressor(); String data = "aabcccccaaa"; compressor.setCompressionStrategy(new RunLengthEncoding()); System.out.println( "RLE Compression: " + compressor.compress(data) ); // RLE Compression: a2b1c5a3 compressor.setCompressionStrategy(new SimpleReplacementCompression()); System.out.println( "Simple Replacement: " + compressor.compress(data) ); // Simple Replacement: 11bccccc111 } } Strategy 패턴 요약 Strategy 패턴은 동일한 작업을 수행하는 여러 방법을 정의하고, 실행 시 적합한 방법을 동적으로 선택하여 사용하는 행동 패턴이다.
back-end
· 2024-11-20
[design-pattern] facade 패턴
intro : facade 패턴에 대해서 알아보자 Facade 패턴이란 ? Facade 란 프랑스어로 어떤 건물의 외벽을 뜻하는 말이다. 외벽을 뜻하는 단어로 이루어진 디자인 패턴은 어떤 패턴일까 ? 좀 더 자세히 알아보자면, 구조적 디자인 패턴 중 하나로 복잡한 서브시스템을 단순화하여 사용하는 패턴이라고 볼 수 있다. 이 패턴은 클라이언트가 복잡한 시스템이나 여러 클래스를 직접 다루지 않고, 단일 인터페이스를 통해 시스템을 간단히 사용할 수 있도록 해준다. 즉, 복잡한 내부 시스템을 숨기고, 간단하고 일관된 인터페이스를 제공하는 것이 Facade 패턴의 핵심이라고 볼 수 있다. Facade 패턴은 언제 사용하는가 ? Facade 패턴은 복잡한 서브시스템의 인터페이스를 단순화하여 클라이언트가 쉽게 접근할 수 있도록 할 때 사용한다. 이 패턴은 클라이언트와 복잡한 서브시스템 간에 하나의 통합된 진입점을 제공함이 중요한 특징이다. Facade 패턴 적용 전 (1) 어떠한 클라이언트는 아침에 일어나면 온도를 조절하고, 불을 켜고, 커피를 마신다. 이러한 행동은 다음과 같이 각 클래스의 메소드들로 나누어져 있으며, 객체를 생성하여 각각 호출하여야 한다. public class Thermostat { public void setTemperature(int temperature) { System.out.println("Setting thermostat to " + temperature + " degrees."); } } public class Lights { public void on() { System.out.println("Lights are on."); } public void off() { System.out.println("Lights are off."); } } public class CoffeeMaker { public void brewCoffee() { System.out.println("Brewing coffee."); } } 실제로 클라이언트는 각 클래스의 메소드들을 어떻게 호출해야 하는지 다음과 같은 코드를 보면 알 수 있다. // Client code public class Main { public static void main(String[] args) { Thermostat thermostat = new Thermostat(); // 온도 class 객체 생성 Lights lights = new Lights(); // 불 class 객체 생성 CoffeeMaker coffeeMaker = new CoffeeMaker(); // 커피 머신 class 객체 생성 thermostat.setTemperature(25); // 온도 25도 설정 lights.on(); // 불 전원 on coffeeMaker.brewCoffee(); // 커미 끓이기 } } Facade 패턴 적용 후 (1) 기존에 각 클래스들로 나누어져 있던 것들을 하나로 모은 클래스 SmartHomeFacade 생성하여 클라이언트가 wakeUp, leaveHome 메소드를 통해 위 3개의 클래스를 제어 할 수 있도록 기능을 제공한다. public class SmartHomeFacade { private Thermostat thermostat; private Lights lights; private CoffeeMaker coffeeMaker; public SmartHomeFacade(Thermostat thermostat, Lights lights, CoffeeMaker coffeeMaker) { this.thermostat = thermostat; this.lights = lights; this.coffeeMaker = coffeeMaker; } public void wakeUp() { System.out.println("Waking up..."); thermostat.setTemperature(22); lights.on(); coffeeMaker.brewCoffee(); } public void leaveHome() { System.out.println("Leaving home..."); thermostat.setTemperature(18); lights.off(); } } 클라이언트는 아래 코드에서 보듯이 Thermostat Lights CoffeeMaker 클래스들의 각 역할을 알지 못하여도, 일관된 인터페이스로 제공된 SmartHomeFacade 객체의 wakeUp, leaveHome 메소드들을 통해 쉽고 간단하게 제어 할 수 있다. 이게 Facade 패턴의 장점이다. // Client code public class Main { public static void main(String[] args) { Thermostat thermostat = new Thermostat(); Lights lights = new Lights(); CoffeeMaker coffeeMaker = new CoffeeMaker(); SmartHomeFacade smartHome = new SmartHomeFacade(thermostat, lights, coffeeMaker); smartHome.wakeUp(); smartHome.leaveHome(); } } Facade 패턴 적용 전 (2) 다른 예제로도 패턴 적용을 해보도록 하겠다. 다음과 같이 각 클래스가 FileReader, FileWriter, FileDeleter 3개로 나누어져 있다고 상황을 가정해 보자. class FileReader { public String readFile(String filePath) throws IOException { return new String(Files.readAllBytes(Paths.get(filePath))); } } class FileWriter { public void writeFile(String filePath, String content) throws IOException { Files.write(Paths.get(filePath), content.getBytes()); } } class FileDeleter { public void deleteFile(String filePath) throws IOException { Files.delete(Paths.get(filePath)); } } 이걸 만약에 클라이언트에서 각각의 클래스를 생성해서 사용하려면? 다음 코드와 같이 객체를 따로 생성해서 메소드를 각각의 클래스에 맞게 상황에 따로 호출해줘야 한다. // Client code public class Main { public static void main(String[] args) throws IOException { FileReader fileReader = new FileReader(); FileWriter fileWriter = new FileWriter(); FileDeleter fileDeleter = new FileDeleter(); String readFile = fileReader.readFile("test.txt"); fileWriter.writeFile("test.txt", "쓰고싶은 문자열"); fileDeleter.deleteFile("test.txt"); } } Facade 패턴 적용 후 (2) Facade 패턴을 적용하면 내부 시스템의 클래스 객체를 생성하는 것 까지도 다음과 같이 처리 할 수 있다. 해당 객체를 생성하는 시점에서 필요한 서브시스템의 클래스들을 생성자에서 생성해버린다. class FileSystemFacade { private FileReader fileReader; private FileWriter fileWriter; private FileDeleter fileDeleter; public FileSystemFacade() { this.fileReader = new FileReader(); this.fileWriter = new FileWriter(); this.fileDeleter = new FileDeleter(); } public String readFile(String filePath) { try { return fileReader.readFile(filePath); } catch (IOException e) { System.err.println("Error reading file: " + e.getMessage()); return null; } } public boolean writeFile(String filePath, String content) { try { fileWriter.writeFile(filePath, content); return true; } catch (IOException e) { System.err.println("Error writing file: " + e.getMessage()); return false; } } public boolean deleteFile(String filePath) { try { fileDeleter.deleteFile(filePath); return true; } catch (IOException e) { System.err.println("Error deleting file: " + e.getMessage()); return false; } } } 클라이언트는 아래 코드에서 보듯이 FileSystemFacade 클래스의 객체만 생성하며 서브시스템의 복잡도는 줄이고 높은 편의성을 제공하는 것을 알 수 있다. // Client code public class Main { public static void main(String[] args) { FileSystemFacade fs = new FileSystemFacade(); // Write to file boolean writeSuccess = fs.writeFile("test.txt", "Hello, Facade Pattern!"); System.out.println("File write success: " + writeSuccess); // Read from file String content = fs.readFile("test.txt"); System.out.println("File content: " + content); // Delete file boolean deleteSuccess = fs.deleteFile("test.txt"); System.out.println("File delete success: " + deleteSuccess); } } Facade 패턴 요약 Facade 패턴은 복잡한 시스템을 단순화하고, 내부 시스템에 대한 의존성을 줄이며, 클라이언트가 쉽게 사용할 수 있도록 편의성을 제공 해주는 구조적 디자인 패턴이다.
back-end
· 2024-11-15
<
>
Touch background to close