CS/디자인패턴

[디자인패턴] Abstract Factory 패턴 (추상 팩토리 패턴)

mabb 2023. 6. 8. 17:08
반응형

▶디자인 패턴 ( Design Pattern)

:프로그램 개발 시 문제 해결을 위하여 빈번히 사용되는 개발자들의 경험, 내적인 축적에 대하여,
GoF(Gang of Four) 라 불리는 4인의 개발자들이 각각을 패턴으로 정의하고 이름을 붙였다.
이를 디자인 패턴 (Design Pattern) 이라고 한다.
 23개의 디자인 패턴을
『Elements of Reusable Object-Oriented Software』
라는 책으로 발간하였다.

Gang of Four 분들은 모두 안경을 썼다.

 

▷디자인 패턴의 용어를 빌리면 서로의 아이디어를 보다 용이하게 비교, 논의할 수 있게 된다.
▷재사용과 기능확장이 쉬운 소프트웨어를 만들기 위한 유익한 기법이 바로 디자인 패턴이다.

 

---------------------------------------------------------
1. Iterator
2. Adapter
3. Template Method
4. Factory Method
5. Singleton
6. Prototype
7. Builder
8. Abstract Factory
9. Bridge
10. Strategy
11. Composite
12. Decorator
13. Visitor
14. Chain of Responsibility
15. Facade
16. Mediator
17. Observer
18. Memento
19. State
20. Flyweight
21. Proxy
22. Command
23. Interpreter
---------------------------------------------------------

▶Abstract Factory 패턴

"제품군, 패밀리 생성"
"인스턴스 세트를 생성"


-왜 사용하는가
-클래스다이어그램
-각 클래스(인터페이스)의 역할
-Java소스

▶Abstract Factory 패턴에 대한 이해

-제품군(패밀리)을 생성하는 패턴이다.

-추상적인 팩토리는 추상적인 부품으로 추상적인 제품을 만든다.

-클라이언트는 추상적인 팩토리의 기능을 사용한다.

-추상적인 팩토리에 동적으로 대입되는 구체적인 팩토리에 따라 해당 팩토리에서 생성하는 제품군(패밀리) 객체 세트를 생성할 수 있다.

-클라이언트는 추상적인 팩토리에 대입되는 구체적인 팩토리에 해당하는 제품군을 생성할 수 있다.

-Class 클래스의 forName메소드로 클래스를 동적으로 읽을 수 있다.

-교재의 예재에서는 링크 정보를 List 형태의 HTML로 만들거나 Table 형태의 HTML로 만든다. List를 만드는 구체적인 팩토리, 테이블을 만드는 구체적인 팩토리에 따라 생성되는 결과물이 달라진다.

-직접 만들어본 예제에서 팩토리는 조깅할 때 필요한 제품을 생산한다.

-추상적인 공장은 추상적인 신발, 바지, 바람막이를 생성한다.

-구체적인 나이키 공장, 아디다스 공장, 퓨마 공장에서는 구체적인 각각의 브랜드 제품을 생성한다.

-클라이언트는 구체적인 공장과 아무런 연관 없이 동적으로 생성하는 제품군을 변경할 수 있다.

-추상적인 공장 = 나이키 공장이 될 때  클라이언트는 나이키 신발, 나이키 팬츠, 나이키 바람막이를 생성하여 사용할 수 있다.

 

▶왜 사용하는가

클라이언트 단의 소스 변경 없이 선택하는 '구체적인 공장'에 따라 해당 공장에 해당되는 제품군(패밀리) 객체를 생성할 수 있다. 대표적인 예시로 시스템에서 사용하는 { 버튼, 아이콘, 폴더 }의 제품군(객체군, 세트)이/가 필요하다고 할 때 사용하는 시스템이 [Android] 이냐 [IOS] 이냐에 따라 제품군은 해당 시스템용 세트로 만들 필요가 있다. 이때 추상 공장에 [안드로이드 구체적 공장] 이 대입되면 {안드로이드용 버튼, 안드로이드용 아이콘, 안드로이드용 폴더}를 생성하고, 추상 공장에 [IOS 구체적 공장]이 대입되면 {IOS용 버튼, IOS용아이콘, IOS용 폴더}가 생성된다. 이러한 '세트(패밀리)' 제품군을 생성하기 위해서 클라이언트의 코드는 하나도 변경할 필요가 없다. 클라이언트는 오직 '추상적'인 것에 의존한다.

 

▶클래스다이어그램

Abstract Factory 패턴의 클래스다이어그램

 

▶각 클래스(인터페이스)의 역할

"추상 팩토리는 추상 부품으로 추상 제품을 생성한다"
"클라이언트는 추상 팩토리에만 의존한다"

▷추상 팩토리
추상 부품으로 추상제품을 만든다.

▷추상 부품
추상 제품의 재료가 되는 추상적인 부품 역할이다.

▷추상 제품
추상 부품을 조합하여 만들어지는 제품, 추상 부품을 공통으로 다루기 위한 인터페이스 역할을 한다.

▷클라이언트
추상 팩토리를 동적으로 구체화하고, 추상 팩토리의 기능만 사용한다. 구체적인 공장은 알지 못한다.

▷구체적 팩토리
추상 팩토리를 구체적으로 구현한다. 구체적인 제품을 생산한다.

▷구체적 부품
 구체적인 제품의 재료가 된다

▷구체적 제품
구체적인 부품을 조립하여 만드는 구체적인 제품이다.

 

 

▶Java 소스

*java1.8 이전 버전을 사용하는 교재를 참고 하였습니다.
Java API 문서 등을 참조하여 제네릭을 적용하는 등 소스를 개선할 필요가 있습니다.
(https://docs.oracle.com/en/java/javase/20/docs/api/)

▷ ListFactory와 TableFactory를 가지는 예제

 

추상적 공장과 추상적 부품, 제품======================================================

▷Factory.java

package main.java.designpattern.abstractfactory1.factory;

public abstract class Factory {
    public static Factory getFactory(String classname){
        Factory factory = null;
        try {
            factory = (Factory)Class.forName(classname).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            System.out.println(classname + " 클래스가 발견되지 않았습니다.");
        }
        return factory;
    }
    public abstract Link createLink(String caption, String url);
    public abstract Tray createTray(String caption);
    public abstract Page createPage(String title, String author);
}

 

▷Item.java

package main.java.designpattern.abstractfactory1.factory;

public abstract class Item {
    protected String caption;
    public Item(String caption){
        this.caption = caption;
    }
    public abstract String makeHTML();
}

 

▷Link.java

package main.java.designpattern.abstractfactory1.factory;

public abstract class Link extends Item{
    protected String url;
    public Link(String caption, String url) {
        super(caption);
        this.url = url;
    }
}

 

▷Page.java

package main.java.designpattern.abstractfactory1.factory;

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

public abstract class Page {
    protected String title;
    protected String author;
    protected List content = new ArrayList();
    public Page(String title, String author){
        this.title = title;
        this.author = author;
    }
    public void add(Item item){
        content.add(item);
    }
    public void output(){
        String filename = title  + ".html";
        try {
            Writer writer = new FileWriter(filename);
            writer.write(this.makeHTML());
            writer.close();
            System.out.println(filename + " 을 작성하였습니다.");
            System.out.println(this.makeHTML());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public abstract String makeHTML();
}

 

▷Tray.java

package main.java.designpattern.abstractfactory1.factory;

import java.util.ArrayList;
import java.util.List;

public abstract class Tray extends Item{
    protected List tray = new ArrayList();
    public Tray(String caption){
        super(caption);
    }
    public void add(Item item){
        tray.add(item);
    }
}

 

 

구체적 공장과 구체적 부품, 제품 ListFactory======================================================

 

▷ListFactory.java

package main.java.designpattern.abstractfactory1.listfactory;

import main.java.designpattern.abstractfactory1.factory.*;

public class ListFactory extends Factory {
    @Override
    public Link createLink(String caption, String url) {
        return new ListLink(caption, url);
    }

    @Override
    public Tray createTray(String caption) {
        return new ListTray(caption);
    }

    @Override
    public Page createPage(String title, String author) {
        return new ListPage(title, author);
    }
}

 

 

▷ListLink.java

package main.java.designpattern.abstractfactory1.listfactory;


import main.java.designpattern.abstractfactory1.factory.*;

public class ListLink extends Link {
    public ListLink(String caption, String url) {
        super(caption, url);
    }
    @Override
    public String makeHTML() {
        return "<li><a href=\"" + url + "\">" + caption + "</a></li>\n";
    }
}

 

 

▷ListPage.java

package main.java.designpattern.abstractfactory1.listfactory;

import main.java.designpattern.abstractfactory1.factory.*;
import java.util.Iterator;

public class ListPage extends Page {
    public ListPage(String title, String author){
        super(title,author);
    }
    @Override
    public String makeHTML() {
        StringBuffer buffer = new StringBuffer();
        buffer.append("<html><head><title>" + title + "</title></head>\n");
        buffer.append("<body>\n");
        buffer.append("<h1>" + title + "</h1>\n");
        buffer.append("<ul>\n");
        Iterator it = content.iterator();
        while(it.hasNext()){
            Item item = (Item)it.next();
            buffer.append(item.makeHTML());
        }
        buffer.append("</ul>\n");
        buffer.append("<hr><adress>" + author + "</address>");
        buffer.append("</body></html>\n");
        return buffer.toString();
    }
}

 

 

▷ListTray.java

package main.java.designpattern.abstractfactory1.listfactory;

import main.java.designpattern.abstractfactory1.factory.*;
import java.util.Iterator;

public class ListTray extends Tray {
    public ListTray(String caption){
        super(caption);
    }
    @Override
    public String makeHTML() {
        StringBuffer buffer = new StringBuffer();
        buffer.append("<li>\n");
        buffer.append(caption + "\n");
        buffer.append("<ul>\n");
        Iterator it = tray.iterator();
        while(it.hasNext()){
            Item item = (Item)it.next();
            buffer.append(item.makeHTML());
        }
        buffer.append("</ul>\n");
        buffer.append("</li>\n");
        return buffer.toString();
    }
}

 

구체적 공장과 구체적 부품, 제품 TableFactory======================================================

 

▷TableFactory.java

package main.java.designpattern.abstractfactory1.tablefactory;

import main.java.designpattern.abstractfactory1.factory.*;

public class TableFactory extends Factory {
    @Override
    public Link createLink(String caption, String url) {
        return new TableLink(caption, url);
    }
    @Override
    public Tray createTray(String caption) {
        return new TableTray(caption);
    }

    @Override
    public Page createPage(String title, String author) {
        return new TablePage(title,author);
    }
}

 

 

▷TableLink.java

package main.java.designpattern.abstractfactory1.tablefactory;

import main.java.designpattern.abstractfactory1.factory.*;

public class TableLink extends Link {
    public TableLink(String caption, String url){
        super(caption,url);
    }
    @Override
    public String makeHTML() {
        return "<td><a href=\"" + url + "\">" + caption + "</a></td>\n";
    }
}

 

 

▷TablePage.java

package main.java.designpattern.abstractfactory1.tablefactory;

import main.java.designpattern.abstractfactory1.factory.*;

import java.util.Iterator;

public class TablePage extends Page {
    public TablePage(String title, String author){
        super(title, author);
    }
    @Override
    public String makeHTML() {
        StringBuffer buffer = new StringBuffer();
        buffer.append("<html><head><title>" + title + "</title></head>\n");
        buffer.append("<body>\n");
        buffer.append("<h1>" + title + "</h1>\n");
        buffer.append("<table width=\"80%\" border=\"3\">\n");
        Iterator it = content.iterator();
        while(it.hasNext()){
            Item item = (Item) it.next();
            buffer.append("<tr>" + item.makeHTML() + "</tr>");
        }
        buffer.append("</table>\n");
        buffer.append("<hr><address>" + author + "</address>");
        buffer.append("</body></html>\n");
        return buffer.toString();
    }
}

 

 

▷TableTray.java

package main.java.designpattern.abstractfactory1.tablefactory;

import main.java.designpattern.abstractfactory1.factory.*;

import java.util.Iterator;

public class TableTray extends Tray {
    public TableTray(String caption){
        super(caption);
    }
    @Override
    public String makeHTML() {
        StringBuffer buffer = new StringBuffer();
        buffer.append("<td>");
        buffer.append("<table width=\"100%\" border=\"1\"><tr>");
        buffer.append("<td bgcolor=\"#cccccc\" align=\"center\" colspan=\"" + tray.size() + "\"><b>" + caption + "</b></td>");
        buffer.append("</tr>\n");
        buffer.append("<tr>\n");
        Iterator it = tray.iterator();
        while(it.hasNext()){
            Item item = (Item) it.next();
            buffer.append(item.makeHTML());
        }
        buffer.append("</tr></table>");
        buffer.append("</td>");
        return buffer.toString();
    }
}

 

 

 

 

▷ NikeFactory와 AdidasFactory와 PumaFactory를 가지는 예제

 

추상적 공장과 추상적 부품, 제품======================================================

 

▷Factory.java

package main.java.designpattern.abstractfactory2.factory;

public abstract class Factory {
    public static Factory getFactory(String factoryName){
        try {
            Factory factory = null;
            factory = (Factory) Class.forName(factoryName).newInstance();
            return factory;
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            System.out.println("클래스 이름을 다시 입력해주세요.");
            throw new RuntimeException(e);
        }
    }
    public abstract JoggerItem produceShoes(String kind, String brand, String shoesSize);
    public abstract JoggerItem producePants(String kind, String brand, String bottomSize);
    public abstract JoggerItem produceWindBreaker(String kind, String brand, String topSize);
}

 

▷JoggerItem.java

package main.java.designpattern.abstractfactory2.factory;

public abstract class JoggerItem {
    protected String kind;
    protected String brand;
    public JoggerItem(String kind, String brand){
        this.kind = kind;
        this.brand = brand;
    }
    @Override
    public abstract String toString();
}

 

▷Pants.java

package main.java.designpattern.abstractfactory2.factory;

public abstract class Pants extends JoggerItem{
    protected String bottomSize;
    public Pants(String kind, String brand, String bottomSize){
        super(kind,brand);
        this.bottomSize = bottomSize;
    }
}

 

 

▷Shoes.java

package main.java.designpattern.abstractfactory2.factory;

public abstract class Shoes extends JoggerItem{
    protected String shoesSize;
    public Shoes(String kind, String brand, String shoesSize){
        super(kind,brand);
        this.shoesSize = shoesSize;
    }
}

 

 

▷WindBreaker.java

package main.java.designpattern.abstractfactory2.factory;

public abstract class WindBreaker extends JoggerItem{
    protected String topSize;
    public WindBreaker(String kind, String brand, String topSize){
        super(kind,brand);
        this.topSize = topSize;
    }
}

 

구체적 공장과 구체적 부품, 제품 NikeFactory======================================================

 

▷NikeFactory.java

package main.java.designpattern.abstractfactory2.nike;

import main.java.designpattern.abstractfactory2.factory.Factory;
import main.java.designpattern.abstractfactory2.factory.JoggerItem;

public class NikeFactory extends Factory {
    @Override
    public JoggerItem produceShoes(String kind, String brand, String shoesSize) {
        return new NikeShoes(kind,brand,shoesSize);
    }

    @Override
    public JoggerItem producePants(String kind, String brand, String bottomSize) {
        return new NikePants(kind,brand,bottomSize);
    }

    @Override
    public JoggerItem produceWindBreaker(String kind, String brand, String topSize) {
        return new NikeWindBreaker(kind, brand, topSize);
    }
}

 

 

▷NikePants.java

package main.java.designpattern.abstractfactory2.nike;

import main.java.designpattern.abstractfactory2.factory.JoggerItem;
import main.java.designpattern.abstractfactory2.factory.Pants;

public class NikePants extends Pants {
    public NikePants(String kind, String brand, String bottomSize) {
        super(kind, brand, bottomSize);
    }
    @Override
    public String toString() {
        return "브랜드: ✌" + brand + "\n" +
                "품목: " + kind + "\n" +
                "사이즈:" + bottomSize +"\n";
    }
}

 

 

▷NikeShoes.java

package main.java.designpattern.abstractfactory2.nike;

import main.java.designpattern.abstractfactory2.factory.JoggerItem;
import main.java.designpattern.abstractfactory2.factory.Shoes;

public class NikeShoes extends Shoes {
    public NikeShoes(String kind, String brand, String shoesSize) {
        super(kind, brand, shoesSize);
    }
    @Override
    public String toString() {

        return "브랜드: ✌" + brand + "\n" +
                "품목: " + kind + "\n" +
                "사이즈:" + shoesSize +"\n";
    }
}

 

 

▷NikeWindBreaker.java

package main.java.designpattern.abstractfactory2.nike;

import main.java.designpattern.abstractfactory2.factory.JoggerItem;
import main.java.designpattern.abstractfactory2.factory.WindBreaker;

public class NikeWindBreaker extends WindBreaker {
    public NikeWindBreaker(String kind, String brand, String topSize) {
        super(kind, brand, topSize);
    }
    @Override
    public String toString() {
        return "브랜드: ✌" + brand + "\n" +
                "품목: " + kind + "\n" +
                "사이즈:" + topSize +"\n";
    }
}

 

구체적 공장과 구체적 부품, 제품 AdidasFactory======================================================

 

▷AdidasFactory.java

package main.java.designpattern.abstractfactory2.adidas;

import main.java.designpattern.abstractfactory2.factory.*;

public class AdidasFactory extends Factory {
    @Override
    public JoggerItem produceShoes(String kind, String brand, String shoesSize) {
        return new AdidasShoes(kind, brand, shoesSize);
    }

    @Override
    public JoggerItem producePants(String kind, String brand, String bottomSize) {
        return new AdidasPants(kind, brand, bottomSize);
    }

    @Override
    public JoggerItem produceWindBreaker(String kind, String brand, String topSize) {
        return new AdidasWindBreaker(kind, brand, topSize);
    }
}

 

 

▷AdidasPants.java

package main.java.designpattern.abstractfactory2.adidas;

import main.java.designpattern.abstractfactory2.factory.JoggerItem;
import main.java.designpattern.abstractfactory2.factory.Pants;

public class AdidasPants extends Pants {
    public AdidasPants(String kind, String brand, String bottomSize) {
        super(kind, brand, bottomSize);
    }
    @Override
    public String toString() {

        return "브랜드: 🔥" + brand + "\n" +
                "품목: " + kind + "\n" +
                "사이즈:" + bottomSize +"\n";
    }
}

 

 

▷AdidasShoes.java

package main.java.designpattern.abstractfactory2.adidas;

import main.java.designpattern.abstractfactory2.factory.JoggerItem;
import main.java.designpattern.abstractfactory2.factory.Shoes;

public class AdidasShoes extends Shoes {
    public AdidasShoes(String kind, String brand, String shoesSize) {
        super(kind, brand, shoesSize);
    }
    @Override
    public String toString() {

        return "브랜드: 🔥" + brand + "\n" +
                "품목: " + kind + "\n" +
                "사이즈:" + shoesSize +"\n";
    }
}

 

 

▷AdidasWindBreaker.java

package main.java.designpattern.abstractfactory2.adidas;

import main.java.designpattern.abstractfactory2.factory.JoggerItem;
import main.java.designpattern.abstractfactory2.factory.WindBreaker;

public class AdidasWindBreaker extends WindBreaker {
    public AdidasWindBreaker(String kind, String brand, String topSize) {
        super(kind, brand, topSize);
    }
    @Override
    public String toString() {

        return "브랜드: 🔥" + brand + "\n" +
                "품목: " + kind + "\n" +
                "사이즈:" + topSize +"\n";
    }
}

 

 

구체적 공장과 구체적 부품, 제품 PumaFactory======================================================

 

▷PumaFactory.java

package main.java.designpattern.abstractfactory2.puma;

import main.java.designpattern.abstractfactory2.factory.Factory;
import main.java.designpattern.abstractfactory2.factory.JoggerItem;

public class PumaFactory extends Factory {
    @Override
    public JoggerItem produceShoes(String kind, String brand, String shoesSize) {
        return new PumaShoes(kind, brand, shoesSize);
    }
    @Override
    public JoggerItem producePants(String kind, String brand, String bottomSize) {
        return new PumaPants(kind, brand, bottomSize);
    }
    @Override
    public JoggerItem produceWindBreaker(String kind, String brand, String topSize) {
        return new PumaWindBreaker(kind, brand, topSize);
    }
}

 

 

▷PumaPants.java

package main.java.designpattern.abstractfactory2.puma;

import main.java.designpattern.abstractfactory2.factory.JoggerItem;
import main.java.designpattern.abstractfactory2.factory.Pants;

public class PumaPants extends Pants {
    public PumaPants(String kind, String brand, String bottomSize) {
        super(kind, brand, bottomSize);
    }
    @Override
    public String toString() {

        return "브랜드: 🐆" + brand + "\n" +
                "품목: " + kind + "\n" +
                "사이즈:" + bottomSize +"\n";
    }
}

 

 

▷PumaShoes.java

package main.java.designpattern.abstractfactory2.puma;

import main.java.designpattern.abstractfactory2.factory.JoggerItem;
import main.java.designpattern.abstractfactory2.factory.Shoes;

public class PumaShoes extends Shoes {
    public PumaShoes(String kind, String brand, String shoesSize) {
        super(kind, brand, shoesSize);
    }
    @Override
    public String toString() {

        return "브랜드: 🐆" + brand + "\n" +
                "품목: " + kind + "\n" +
                "사이즈:" + shoesSize +"\n";
    }
}

 

 

▷PumaWindBreaker.java

package main.java.designpattern.abstractfactory2.puma;

import main.java.designpattern.abstractfactory2.factory.JoggerItem;
import main.java.designpattern.abstractfactory2.factory.WindBreaker;

public class PumaWindBreaker extends WindBreaker {
    public PumaWindBreaker(String kind, String brand, String topSize) {
        super(kind, brand, topSize);
    }
    @Override
    public String toString() {

        return "브랜드: 🐆" + brand + "\n" +
                "품목: " + kind + "\n" +
                "사이즈:" + topSize +"\n";
    }
}

 

 

조거 아이템 생산 추상팩토리 패턴 실행 결과======================================================

구체적 공장의 클래스 이름을 입력한다.

 

아디다스공장에서 조거 세트를 생산하였다.

 

 

나이키공장에서 조거 세트를 생산하였다.

 

퓨마공장에서 조거 세트를 생산하였다.

 

 

 

[디자인패턴] 생성패턴) 추상 팩토리 패턴(Abstract Factory Pattern)

1. 추상 팩토리 패턴 (Abstract Factory Pattern) : 객체 생성을 위한 디자인 패턴이다. 관련 객체들의 구상 클래스들을 지정하지 않고도 관련 객체들의 모음(나이키세트 or 아디다스세트 or 퓨마세트)을

mabb.tistory.com

---------------------------------------------------------
※참고자료
-Java언어로 배우는 디자인 패턴 입문
-Headfirst Design Pattern
-나무위키, 위키백과, 네이버백과
-리팩토링구루(https://refactoring.guru/ko)

반응형