본문 바로가기
3학년 2학기 학사/소프트웨어 분석 및 설계

[소프트웨어 분석 및 설계] L16. Abstract Factory Pattern, Builder Pattern, Prototype Pattern

by whiteTommy 2024. 12. 13.
반응형

Abstract Factory Pattern

  • 추상 팩토리 패턴이란?
    • 연관 있는 여러 객체 (제품) 군 (family) 의 생성을 추상화 한 생성 패턴
      • 팩토리 메소드 패턴은 단일 객체 (제품) 의 생성을 추상화 한 것이라면, 추상 팩토리 패턴은 관련이 있는 여러 객체 (제품) 들의 일관된 생성을 추상화한 것
    • 연관 있는 제품 군의 예시
      • {Chair, Sofa, CoffeeTable} 에 대해 스타일 별로 각 제품을 일관되게 생성 해야 함
  • 추상 팩토리 패턴
    • 새로운 스타일의 제품 군이 추가되더라도 기존의 코드를 바꾸지 않고 추가할 수 있어야 함
      • 제품 별로 인터페이스를 추상화 하고, 모든 추상 제품에 대한 생성 메소드를 가지는 팩토리로 추상화
      • 각 서브 팩토리에서는 스타일에 연관된 제품군이 생성될 수 있도록 생성 메소드를 구현
  • 추상 팩토리 패턴의 구조
  • 추상 팩토리 패턴 : Cross-platform GUI example 


  • 추상 팩토리 패턴 장단점
    • 장점
      • 객체를 생성하는 코드를 분리하여 클라이언트 코드와 결합도를 낮출 수 있음
      • 제품 군 (family) 를 쉽게 대체할 수 있음
      • SRP, OCP 준수
    • 단점
      • 제품, 팩토리 들을 모두 구현해주어야 하기 때문에 코드 구조 복잡성이 높음 (팩토리 메소드 패턴과 동일)
      • 새로운 제품 추가 시 모든 팩토리 구현 로직에 새로운 생성 함수가 추가되어야 함

 

 

Builder Pattern

  • 빌더 패턴이란?
    • 복잡한 객체 생성 과정과 표현 방법을 분리하여 클라이언트가 다양한 구성을 조합하여 객체를 생성할 수 있도록 하는 생성 패턴
    • 만약, 객체를 생성하고 초기화 하려고 할 때, 해당 클래스의 멤버 변수가 많은 경우에 모든 멤버 변수를 생성자에 넣어야 할까?
      • 특정 객체 생성 시 모든 멤버 변수의 값이 쓰이지 않는다면? 
  • 빌더 패턴
    • 클래스 내에서 객체 생성에 관련된 코드를 빼내서 Builder 라는 별도의 객체로 옮기고, Builder 를 이용하여 값을 설정 할 수 있게 함
      •  복잡한 객체를 만드는 프로세스를 독립적으로 분리

        HouseBuilder builder = new HouseBuilder();
        House house1 = builder.buildWalls(4).buildDoors(1).getResult();
        
        builder.reset();
        House house2 = builder.buildWalls(8).buildDoors(2).buildWindows(4).getResult();
  • 빌더 패턴의 구조
  • 빌더 패턴의 구조 예시
  • 빌더 패턴 구현 예시
    public class House{
        private int roofs;
        private int walls;
        private int windows;
        
        //getters and setters
    }
    
    public interface Builder{
        void reset();
        Builder setRoofs(int roofs);
        Builder setWalls(int walls);
        builder setWindows(int windows);
    }
    
    public class HouseBuilder implements Builder{
        private House house;
        
        public HouseBuilder(){
        	this.house = new House();
        }
        
        public void reset(){
        	this.house = new House();
        }
        
        public Builder setRoofs(int roofs){
        	this.house.setRoofs(roofs);
            return this;
        }
        
        public Builder setWalls(int walls){
        	this.house.setWalls(walls);
            return this;
        }
        
        public Builder setWindows(int windows){
        	this.house.setWindows(windows);
            return this;
        }
        
        public House getResult(){
        	return this.house;
        }
    }
    
    public class Director{
        public void makeSimpleHouse(Builder builder){
        	builder.reset();
            builder.setRoofs(1);
        }
        
        public void makeComplexHouse(Builder builder){
        	builder.reset();
            builder.setRoofs(4).setWalls(4).setWindows(4);
        }
    }
    
    public class Client{
        public static void main(String[] args){
        	Director director = new Director();
            HouseBuilder builder = new HouseBuilder();
            
            director.makeSimpleHouse(builder);
            House simpleHouse = builder.getResult();
            
            director.makeComplexHouse(builder);
            House complexHouse = builder.getResult();
        }
    }
  • 빌더 패턴 사용 예시
    public class StringBuilderExample{
        public static void main(String[] args){
        	StringBuilder stringBuilder = new StringBuilder();
            String result = stringBuilder.append("whiteship").append("keesun").toString();
            System.out.println(result);
        }
    }
    
    public class StreamExample{
        public static void main(String[] args){
        	Stream.Builder<String> stringStreamBuilder = Stream.builder();
            Stream<String> names = stringStreamBuilder.add("keesun").add("whiteship").build();
            names.forEach(System.out::println);
        }
    }
  • 빌더 패턴 장단점
    • 장점
      • 구성하기 복잡한 객체를 순차적으로 만들 수 있음
      • 복잡한 객체를 만드는 구체적인 과정을 숨길 수 있음
      • 동일한 프로세스를 통해 구성에 따라 다른 객체를 만들 수 있음
      • 불완전한 객체를 사용하지 못하도록 방지할 수 있음
    • 단점
      • 선행적으로 빌더 객체를 먼저 만들어야 원하는 객체를 만들 수 있음
      • 객체 생성을 위한 코드 대비 구조적으로 복잡해짐
        • 간단한 객체는 생성자를 통해서 만드는게 좋음

 

Prototype Pattern

  • 프로토타입 패턴이란?
    • 기존의 객체를 복제 (clone) 하여 새로운 객체를 만드는 생성 패턴
      • 클래스에 의존하지 않으면서 기존 객체를 복사
      • 원형이 되는 (prototypical) 인스턴스를 사용하여 생성할 객체의 종류를 명시하고, 이렇게 만든 견본을 복사해서 새로운 객체를 생성
    • 객체 생성/복사가 까다로울 수 있는 상황
      • private/protected 멤버 변수 때문에 클래스 외부에서 객체를 복사하지 못할 수 있음
      • 객체 생성에 따른 리소스가 많이 요구될 때 새로 생성하는 것은 비효율
  • 프로토타입 패턴의 구조
    • 복제되는 실제 객체에 복제 프로세스를 위임함 (clone 이라는 공통 인터페이스를 가지게 함)
  • 프로토타입 패턴 구현 예시
    • Cloneable 인터페이스 (in java)
      • 자바에서 Objects.clone() 메소드는 인스턴스 객체의 복제를 위한 메소드
      • 해당 인스턴스를 복제하여 새로운 인스턴스를 생성해 레퍼런스를 반환
      • clone() 메소드를 사용하려면 Cloneable 인터페이스를 implements 하고 clone() 메소드를 오버라이딩 해주어야 함 (Prototype 인터페이스 역할)
        import java.lang.Cloneable;
        
        class Person implements Clonable{
            //멤버 변수...
            //clone 메소드를 오버라이딩
            public Object clone() throws CloneNotSupportedException{
            	// 기본적으로 부모의 clone 을 그대로 불러와 반환 (Shallow copy)
                return super.clone();
            }
        }
        
        public class Main{
            public static void main(String[] args){
            	try{
                	Person p = new Person("James", 11);
                    Person p_copy = (Person)p.clone();
                }catch (Exception e){}
            }
        }


        public class Team implements Cloneable {
            String teamName;
            List<Employee> employeeList;
        
            public Team(String teamName) {
                this.teamName = teamName;
                this.employeeList = new ArrayList<>();
            }
        
            public void addEmployee(Employee employee) {
                employeeList.add(employee);
            }
        
            public List<Employee> getEmployeeList() {
                return employeeList;
            }
        
            @Override
            public Object clone() throws CloneNotSupportedException {
                return super.clone(); // 얕은 복사 수행
            }
        }
        
        public class Employee {
            String name;
        
            public Employee(String name) {
                this.name = name;
            }
        
            @Override
            public String toString() {
                return "Employee{name='" + name + "'}";
            }
        }
        
        public class Client {
            public static void main(String[] args) throws CloneNotSupportedException {
                Team firstTeam = new Team("firstTeam");
                firstTeam.addEmployee(new Employee("James"));
                firstTeam.addEmployee(new Employee("Ted"));
        
                Team copyTeam = (Team) firstTeam.clone(); // shallow copy
        
                System.out.println("Are they the same instance? => " + (firstTeam == copyTeam)); // false
                System.out.println("Do they have the same content? => " + (firstTeam.equals(copyTeam))); // true
        
                System.out.println("firstTeam's members: " + firstTeam.getEmployeeList());
                System.out.println("copyTeam's members: " + copyTeam.getEmployeeList());
        
                firstTeam.getEmployeeList().set(0, new Employee("Gale"));
        
                System.out.println("\nAfter modification:");
                System.out.println("firstTeam's members: " + firstTeam.getEmployeeList());
                System.out.println("copyTeam's members: " + copyTeam.getEmployeeList());
            }
        }


    • Shallow copy (얕은 복사) v.s. deep copy (깊은 복사)
  • 프로토타입 패턴 장단점
    • 장점
      • 프로토타입 패턴을 사용하면 객체를 생성하는 복잡한 과정을 피할 수 있음
      • 객체를 복제하는 것이 객체를 새로 생성하는 것보다 일반적으로 더 빠름
      • 런타임에 객체의 타입을 동적으로 변경하거나 새로운 객체 유형을 추가하는 데 유용
    • 단점
      • 객체가 복잡한 구조를 가지고 있거나 객체 간에 서로 참조가 있을 때, 객체 복제 과정이 복잡해질 수 있음
        • 얕은 복사(객체의 필드만 복제)와 깊은 복사(참조된 객체도 복제)를 관리해야 할 수 있음
      • 프로토타입 패턴은 객체를 복제하므로 남발 시 메모리 사용량이 늘어날 수 있음
      • 객체 복제로 인해 객체의 상태 관리가 어려워질 수 있음

 

 

 

 

 

 

 

 

 

반응형