디자인 패턴에 관한 블로그 시리즈의 시작입니다. 이번 블로그에서는 디자인 패턴의 첫 번째 유형인 Creational Patterns에 대해 설명하겠습니다. 여기에서는 생성 패턴에 해당하는 유형을 실제 사례와 함께 논의합니다. 저는 선택 언어로 Java를 사용할 예정입니다.
디자인 패턴은 소프트웨어 개발에서 중요한 역할을 하며, 일반적인 문제에 대한 검증된 솔루션을 제공하고 모범 사례를 장려합니다. 이는 코드에서 반복되는 디자인 문제를 해결하기 위해 사용자 정의할 수 있는 미리 만들어진 청사진과 같습니다.
객체 지향 프로그래밍에서 생성 디자인 패턴은 객체 인스턴스화를 활용과 분리하여 객체 생성의 유연성과 확장성을 높일 수 있으므로 중요한 역할을 합니다. 이 블로그 게시물은 생성 디자인 패턴의 다섯 가지 주요 유형인 팩토리 메소드(Factory Method), 추상 팩토리(Abstract Factory), 빌더(Builder), 프로토타입(Prototype) 및 싱글톤(Singleton)에 중점을 둘 것입니다. 각각의 작동 방식을 보여주기 위해 Java의 실제 예를 사용하겠습니다.
1. 팩토리 메소드
팩토리 메소드 패턴은 객체를 생성하기 위한 인터페이스를 정의하지만 서브클래스가 생성될 객체의 유형을 변경할 수 있도록 허용합니다. 이 패턴은 Java의 느슨한 결합을 지원하여 애플리케이션별 클래스를 코드에 바인딩할 필요가 없도록 합니다.
실제 시나리오: 트럭, 선박 등 다양한 차량을 이용해 물품을 운송하는 물류 회사를 가정해 보겠습니다. 차량 유형은 필요한 운송 수단에 따라 다릅니다.
// Product Interface interface Transport { void deliver(); } // Concrete Products class Truck implements Transport { @Override public void deliver() { System.out.println("Deliver by land in a truck."); } } class Ship implements Transport { @Override public void deliver() { System.out.println("Deliver by sea in a ship."); } } // Creator abstract class Logistics { public abstract Transport createTransport(); public void planDelivery() { Transport transport = createTransport(); transport.deliver(); } } // Concrete Creators class RoadLogistics extends Logistics { @Override public Transport createTransport() { return new Truck(); } } class SeaLogistics extends Logistics { @Override public Transport createTransport() { return new Ship(); } } // let's call the main class public class Main { public static void main(String[] args) { Logistics logistics = new RoadLogistics(); logistics.planDelivery(); logistics = new SeaLogistics(); logistics.planDelivery(); } }
2. 추상공장
추상 팩토리 패턴은 구체적인 클래스를 지정하지 않고 관련 객체 또는 종속 객체의 패밀리를 생성하기 위한 인터페이스를 제공합니다. 시스템이 객체 생성 방식에 독립적이어야 할 때 유용합니다.
실제 시나리오: 빅토리아풍, 현대풍 등 다양한 유형의 가구 세트를 판매하는 가구점을 상상해 보세요. 각 세트에는 의자, 소파 등의 제품이 포함되어 있습니다.
// Abstract Products interface Chair { void sitOn(); } interface Sofa { void lieOn(); } // Concrete Products class VictorianChair implements Chair { @Override public void sitOn() { System.out.println("Sitting on a Victorian chair."); } } class ModernChair implements Chair { @Override public void sitOn() { System.out.println("Sitting on a Modern chair."); } } class VictorianSofa implements Sofa { @Override public void lieOn() { System.out.println("Lying on a Victorian sofa."); } } class ModernSofa implements Sofa { @Override public void lieOn() { System.out.println("Lying on a Modern sofa."); } } // Abstract Factory interface FurnitureFactory { Chair createChair(); Sofa createSofa(); } // Concrete Factories class VictorianFurnitureFactory implements FurnitureFactory { @Override public Chair createChair() { return new VictorianChair(); } @Override public Sofa createSofa() { return new VictorianSofa(); } } class ModernFurnitureFactory implements FurnitureFactory { @Override public Chair createChair() { return new ModernChair(); } @Override public Sofa createSofa() { return new ModernSofa(); } } // Client code public class Main { private static void createFurniture(FurnitureFactory factory) { Chair chair = factory.createChair(); Sofa sofa = factory.createSofa(); chair.sitOn(); sofa.lieOn(); } public static void main(String[] args) { FurnitureFactory victorianFactory = new VictorianFurnitureFactory(); createFurniture(victorianFactory); FurnitureFactory modernFactory = new ModernFurnitureFactory(); createFurniture(modernFactory); } }
3. 빌더
빌더 패턴은 복잡한 객체의 구성과 표현을 분리하므로 동일한 구성 프로세스로 다양한 표현을 만들 수 있습니다. 이는 선택적인 속성이 많은 객체를 생성하는 데 특히 유용합니다.
실제 시나리오: 고객이 다양한 토핑, 크기, 크러스트 유형으로 피자를 맞춤 설정할 수 있는 온라인 피자 주문 시스템을 생각해 보세요.
// Product class Pizza { private String dough = ""; private String sauce = ""; private String topping = ""; public void setDough(String dough) { this.dough = dough; } public void setSauce(String sauce) { this.sauce = sauce; } public void setTopping(String topping) { this.topping = topping; } @Override public String toString() { return "Pizza [dough=" + dough + ", sauce=" + sauce + ", topping=" + topping + "]"; } } // Builder Interface interface PizzaBuilder { void buildDough(); void buildSauce(); void buildTopping(); Pizza getPizza(); } // Concrete Builders class HawaiianPizzaBuilder implements PizzaBuilder { private Pizza pizza; public HawaiianPizzaBuilder() { this.pizza = new Pizza(); } @Override public void buildDough() { pizza.setDough("cross"); } @Override public void buildSauce() { pizza.setSauce("mild"); } @Override public void buildTopping() { pizza.setTopping("ham+pineapple"); } @Override public Pizza getPizza() { return this.pizza; } } class SpicyPizzaBuilder implements PizzaBuilder { private Pizza pizza; public SpicyPizzaBuilder() { this.pizza = new Pizza(); } @Override public void buildDough() { pizza.setDough("pan baked"); } @Override public void buildSauce() { pizza.setSauce("hot"); } @Override public void buildTopping() { pizza.setTopping("pepperoni+salami"); } @Override public Pizza getPizza() { return this.pizza; } } // Director class Waiter { private PizzaBuilder pizzaBuilder; public void setPizzaBuilder(PizzaBuilder pb) { pizzaBuilder = pb; } public Pizza getPizza() { return pizzaBuilder.getPizza(); } public void constructPizza() { pizzaBuilder.buildDough(); pizzaBuilder.buildSauce(); pizzaBuilder.buildTopping(); } } // Client code public class Main { public static void main(String[] args) { Waiter waiter = new Waiter(); PizzaBuilder hawaiianPizzaBuilder = new HawaiianPizzaBuilder(); PizzaBuilder spicyPizzaBuilder = new SpicyPizzaBuilder(); waiter.setPizzaBuilder(hawaiianPizzaBuilder); waiter.constructPizza(); Pizza pizza1 = waiter.getPizza(); System.out.println("Pizza built: " + pizza1); waiter.setPizzaBuilder(spicyPizzaBuilder); waiter.constructPizza(); Pizza pizza2 = waiter.getPizza(); System.out.println("Pizza built: " + pizza2); } }
4. 프로토타입
프로토타입 패턴은 프로토타입이라고 하는 기존 개체를 복사하여 새 개체를 만드는 데 사용됩니다. 이 패턴은 새로운 객체를 생성하는 데 드는 비용이 클 때 유용합니다.
실제 시나리오: 모양을 생성, 복제, 편집할 수 있는 그래픽 편집기를 생각해 보세요.
import java.util.HashMap; import java.util.Map; // Prototype abstract class Shape implements Cloneable { private String id; protected String type; abstract void draw(); public String getType() { return type; } public String getId() { return id; } public void setId(String id) { this.id = id; } public Object clone() { Object clone = null; try { clone = super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; } } // Concrete Prototypes class Rectangle extends Shape { public Rectangle() { type = "Rectangle"; } @Override public void draw() { System.out.println("Drawing a Rectangle."); } } class Circle extends Shape { public Circle() { type = "Circle"; } @Override public void draw() { System.out.println("Drawing a Circle."); } } // Prototype Registry class ShapeCache { private static Map<String, Shape> shapeMap = new HashMap<>(); public static Shape getShape(String shapeId) { Shape cachedShape = shapeMap.get(shapeId); return (Shape) cachedShape.clone(); } public static void loadCache() { Rectangle rectangle = new Rectangle(); rectangle.setId("1"); shapeMap.put(rectangle.getId(), rectangle); Circle circle = new Circle(); circle.setId("2"); shapeMap.put(circle.getId(), circle); } } // Client code public class Main { public static void main(String[] args) { ShapeCache.loadCache(); Shape clonedShape1 = ShapeCache.getShape("1"); System.out.println("Shape: " + clonedShape1.getType()); Shape clonedShape2 = ShapeCache.getShape("2"); System.out.println("Shape: " + clonedShape2.getType()); } }
5. 싱글톤
싱글턴 패턴은 클래스에 인스턴스가 하나만 있도록 보장하고 이에 대한 전역 액세스 지점을 제공합니다. 이 패턴은 로깅, 캐싱 및 스레드 풀에 일반적으로 사용됩니다.
실제 시나리오: 하나의 인스턴스만 모든 인쇄 작업을 관리해야 하는 프린터 스풀러를 상상해 보세요.
class PrinterSpooler { private static PrinterSpooler instance; private PrinterSpooler() { // private constructor to prevent instantiation } public static PrinterSpooler getInstance() { if (instance == null) { instance = new PrinterSpooler(); } return instance; } public void print(String document) { System.out.println("Printing document: " + document); } } // Client code public class Main { public static void main(String[] args) { PrinterSpooler spooler1 = PrinterSpooler.getInstance(); PrinterSpooler spooler2 = PrinterSpooler.getInstance(); spooler1.print("Document 1"); spooler2.print("Document 2"); System.out.println("Are both spoolers the same instance? " + (spooler1 == spooler2)); } }
https://refactoring.guru/
https://www.javatpoint.com/design-patterns-in-java
https://www.digitalocean.com/community/tutorials/java-design-patterns-example-tutorial
위 내용은 완벽한 코드 생성: 생성 패턴 이해의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!