factory-pattern 추상팩토리 c# - 추상 팩터 리 패턴과 팩토리 방법의 차이점





7 Answers

abstract 팩토리는, 생성해야 할 객체의 메소드를 정의하는 추상 메소드를 사용해, 기본 클래스를 작성합니다. 기본 클래스를 파생시키는 각 팩토리 클래스는 각 객체 유형에 대한 자체 구현을 작성할 수 있습니다.

팩토리 메서드 는 클래스에서 객체를 만드는 데 사용되는 단순한 메서드입니다. 일반적으로 집계 루트에 추가됩니다 ( Order 클래스에는 CreateOrderLine 이라는 메서드가 있음)

초록 공장

아래 예에서 우리는 메시징 시스템에서 큐 생성을 분리 할 수 ​​있도록 인터페이스를 설계하므로 코드 기반을 변경하지 않고도 다른 큐 시스템에 대한 구현을 작성할 수 있습니다.

interface IMessageQueueFactory
{
  IMessageQueue CreateOutboundQueue(string name);
  IMessageQueue CreateReplyQueue(string name);
}

public class AzureServiceBusQueueFactory : IMessageQueueFactory
{
      IMessageQueue CreateOutboundQueue(string name)
      {
           //init queue
           return new AzureMessageQueue(/*....*/);
      }

      IMessageQueue CreateReplyQueue(string name)
      {
           //init response queue
           return new AzureResponseMessageQueue(/*....*/);
      }

}

public class MsmqFactory : IMessageQueueFactory
{
      IMessageQueue CreateOutboundQueue(string name)
      {
           //init queue
           return new MsmqMessageQueue(/*....*/);
      }

      IMessageQueue CreateReplyQueue(string name)
      {
           //init response queue
           return new MsmqResponseMessageQueue(/*....*/);
      }
}

공장 방법

HTTP 서버의 문제는 모든 요청에 ​​대해 항상 응답해야한다는 것입니다.

public interface IHttpRequest
{
    // .. all other methods ..

    IHttpResponse CreateResponse(int httpStatusCode);
}

팩토리 메소드가 없다면, HTTP 서버 사용자 (즉, 프로그래머)는 IHttpRequest 인터페이스의 목적을 IHttpRequest 하는 특정 구현 클래스를 사용해야 할 것입니다.

따라서 팩토리 메소드를 도입하여 응답 클래스 작성을 추상화합니다.

개요

차이점은 팩토리 메소드를 포함하는 클래스의 의도 된 목적객체를 생성하지 않는 반면 추상 팩토리는 객체를 생성하는 데에만 사용해야한다는 것입니다.

객체를 만들 때 LSP ( Liskovs 대체 원칙 )를 깨기 쉽기 때문에 팩토리 메소드를 사용할 때는주의해야합니다.

단점 메소드 템플릿

이 두 패턴의 차이점에 대해 많은 게시물이 있다는 것을 알고 있지만 찾을 수없는 몇 가지 사항이 있습니다.

필자가 읽은 바에 따르면 팩토리 메서드 패턴을 사용하면 하나의 구체적인 제품을 만드는 방법을 정의 할 수 있지만 일반 제품을 볼 때 클라이언트에서 구현을 숨길 수 있습니다. 나의 첫 번째 질문은 추상적 인 공장에 관한 것이다. 하나의 구체적인 객체가 아닌 구체적인 객체의 패밀리를 만들 수있게 해주는 역할입니까? 추상 팩토리는 호출하는 메서드에 따라 매우 큰 객체 하나 또는 많은 객체 만 반환합니까?

마지막 두 가지 질문은 내가 여러 곳에서 본 것을 완전히 이해할 수없는 작은 따옴표에 관한 것입니다.

두 가지의 한 가지 차이점은 Abstract Factory 패턴을 사용하면 클래스가 컴포지션을 통해 다른 객체에 객체 인스턴스화의 책임을 위임하는 반면 Factory Method 패턴은 상속을 사용하고 원하는 객체 인스턴스화를 처리하는 하위 클래스에 의존한다는 것입니다.

필자가 알기로는 팩토리 메서드 패턴에 Creator 인터페이스가있어 ConcreteCreator가 인스턴스화 할 ConcreteProduct를 알 수 있도록 할 것이다. 개체 인스턴스화를 처리하기 위해 상속을 사용하면 이것이 의미하는 바입니까?

이제 해당 견적과 관련하여 Abstract Factory 패턴은 작곡을 통해 객체 인스턴스화의 책임을 다른 객체에 얼마나 위임합니까? 이것은 무엇을 의미 하는가? 초록 공장 패턴도 상속을 사용하여 내 눈에서 건설 프로세스를 수행하는 것처럼 보입니다.하지만 다시 이러한 패턴에 대해 배우고 있습니다.

특히 마지막 질문에 도움이된다면 크게 감사하겠습니다.




Abstract Factory는 관련 제품을 생성하기위한 인터페이스이지만 Factory Method는 하나의 메소드에 불과합니다. Abstract Factory는 여러 Factory Methods로 구현할 수 있습니다.




Abstract Factory와 Factory Method의 주요 차이점은 Abstract Factory는 Composition에 의해 구현 된다는 점입니다. Factory Method는 상속에 의해 구현됩니다 .

네, 올바르게 읽었습니다.이 두 패턴의 주요 차이점은 오래된 구성 대 상속 논쟁입니다.

나는 여러 곳에서 볼 수있는 UML 다이어그램을 여기서 재현하지 않을 것이다. 코드 예제를 제공하고 싶습니다. 그러나이 스레드에서 상위 2 개 답변의 예제를 결합하면 두 가지 답변 중 하나보다 더 나은 데모가 제공됩니다. 또한 (GoF) 서적에 사용 된 용어를 클래스 및 메소드 이름에 추가했습니다.

초록 공장

  1. 여기서 가장 중요한 점은 추상 팩토리가 클라이언트에 삽입 된다는 것입니다. 이것이 추상적 인 공장이 작곡으로 구현되었다고 말하는 이유입니다. 종종 의존성 주입 프레임 워크가 해당 작업을 수행합니다. DI에는 프레임 워크가 필요하지 않습니다.
  2. 두 번째 중요한 점은 여기에있는 구체적인 팩토리 팩토리 메서드 구현 이 아니라는 것입니다. Factory Method의 예제 코드는 아래에 나와 있습니다.
  3. 마지막으로 세 번째 요점은 제품 간의 관계입니다.이 경우 아웃 바운드 및 회신 큐입니다. 하나의 구체적인 팩토리가 Azure 대기열을 생성하고 다른 하나는 다른 MSMQ입니다. GoF는이 제품 관계를 "가족"이라고 부르며이 경우 가족은 클래스 계층 구조를 의미하지 않는다는 것을 알고 있어야합니다.
public class Client {
    private final AbstractFactory_MessageQueue factory;

    public Client(AbstractFactory_MessageQueue factory) {
        // The factory creates message queues either for Azure or MSMQ.
        // The client does not know which technology is used.
        this.factory = factory;
    }

    public void sendMessage() {
        //The client doesn't know whether the OutboundQueue is Azure or MSMQ.
        OutboundQueue out = factory.createProductA();
        out.sendMessage("Hello Abstract Factory!");
    }

    public String receiveMessage() {
        //The client doesn't know whether the ReplyQueue is Azure or MSMQ.
        ReplyQueue in = factory.createProductB();
        return in.receiveMessage();
    }
}

public interface AbstractFactory_MessageQueue {
    OutboundQueue createProductA();
    ReplyQueue createProductB();
}

public class ConcreteFactory_Azure implements AbstractFactory_MessageQueue {
    @Override
    public OutboundQueue createProductA() {
        return new AzureMessageQueue();
    }

    @Override
    public ReplyQueue createProductB() {
        return new AzureResponseMessageQueue();
    }
}

public class ConcreteFactory_Msmq implements AbstractFactory_MessageQueue {
    @Override
    public OutboundQueue createProductA() {
        return new MsmqMessageQueue();
    }

    @Override
    public ReplyQueue createProductB() {
        return new MsmqResponseMessageQueue();
    }
}

공장 방법

  1. 여기서 가장 중요한 포인트는 ConcreteCreator 클라이언트라는 것입니다. 즉, 클라이언트는 부모가 factoryMethod() 정의하는 서브 클래스입니다. 이것이 Factory Method가 상속으로 구현되었다고 말하는 이유입니다.
  2. 두 번째 중요한 점은 팩토리 메서드 패턴이 템플릿 메서드 패턴의 특수화에 지나지 않음을 기억하는 것입니다. 두 패턴은 동일한 구조를 공유합니다. 그들은 단지 목적이 다릅니다. 팩토리 메쏘드는 창조적입니다 (무언가를 만듭니다) 반면에 템플리트 메쏘드는 행동 적입니다 (무언가를 계산합니다).
  3. 마지막으로, 세 번째 요점은 Creator (상위) 클래스가 자체 factoryMethod() 호출한다는 점입니다. 부모 클래스에서 anOperation() 을 제거하고 하나의 메소드 만 남겨두면 더 이상 팩토리 메소드 패턴이 아닙니다. 즉, 팩토리 메소드는 상위 클래스에서 2 개 미만의 메소드로 구현 될 수 없습니다. 하나는 다른 하나를 호출해야합니다.
public abstract class Creator {
    public void anOperation() {
        Product p = factoryMethod();
        p.whatever();
    }

    protected abstract Product factoryMethod();
}

public class ConcreteCreator extends Creator {
    @Override
    protected Product factoryMethod() {
        return new ConcreteProduct();
    }
}

기타. & 잡화 공장 패턴

GoF에서는 두 개의 다른 팩토리 패턴을 정의하지만 이것 만이 존재하는 팩토리 패턴이 아닙니다. 그것들은 반드시 가장 일반적으로 사용되는 팩토리 패턴조차도 아니다. 유명한 세 번째 예제는 Josh Bloch의 Effective Java의 Static Factory Pattern입니다. Head First Design Patterns 책에는 Simple Factory라고하는 또 다른 패턴이 포함되어 있습니다.

모든 공장 ​​패턴이 GoF의 패턴과 일치해야한다고 가정하는 함정에 빠지지 마십시오.




동기의 차이점을 이해하십시오.

객체를 가지고 있고 객체의 상호 관계를 구체적으로 구현 한 툴을 구축한다고 가정합니다. 객체의 변형을 예측하기 때문에 객체의 변형을 만드는 책임을 다른 객체에 할당하여 간접 참조를 작성 했습니다 ( 우리는 추상 팩토리라고 부릅니다 ). 이러한 추상화는 이러한 객체의 변형을 필요로하는 향후 확장을 예측하므로 강력한 이점이 있습니다.

이 생각의 또 다른 다소 흥미로운 동기는 전체 그룹의 모든 대상에 해당하는 변형이있는 경우입니다. 일부 조건에 따라 변형 중 하나가 사용되며 각 경우에 모든 객체는 동일한 변형이어야합니다. 객체의 변형이 공통의 균일 한 계약 ( 더 넓은 의미에서의 인터페이스)을 따르는 한, 구체적인 구현 코드는 결코 깨지지 않아야한다고 생각하기 때문에 이해하기 쉽지 않을 수도 있습니다. 여기서 흥미로운 사실은 기대되는 행동이 프로그래밍 계약에 의해 모델링 될 수없는 경우 항상 그렇지는 않다는 것입니다.

GoF에서 아이디어를 빌리는 간단한 GUI 애플리케이션은 MS 또는 Mac 또는 Fedora OS의 룩앤필을 에뮬레이트하는 가상 모니터라고합니다. 예를 들어 윈도우, 버튼 등과 같은 모든 위젯 객체가 MAC 변형에서 파생 된 스크롤 막대를 제외하고 MS 변형을 가지고있을 때 도구의 목적은 나쁘게 실패합니다.

위의 경우는 추상 팩터 리 패턴 의 기본적인 필요성을 형성합니다.

다른 한편으로, 프레임 워크를 작성하여 많은 사람들이 프레임 워크를 사용하여 다양한 도구 ( 예 : 위의 예제와 같이 )를 작성할 수 있다고 가정 해보십시오. 프레임 워크라는 개념에 의해, 당신은 당신의 논리에서 구체적인 객체를 사용할 수는 없지만 그렇게 할 필요는 없습니다. 오히려 다양한 오브젝트 사이에 몇 가지 상위 레벨 계약을 맺고 어떻게 상호 작용하는지. 프레임 워크 개발자로서 매우 추상적 인 수준을 유지 하는 동안 도구의 각 빌더는 프레임 워크 구조를 따라야합니다. 그러나 도구 작성자 는 어떤 개체를 만들지 결정하고 개체가 만드는 모든 개체가 상호 작용할 방법을 자유롭게 결정할 수 있습니다. 이전 사례 ( 추상 팩토리 패턴 )와 달리, 프레임 워크 작성자로서이 경우 구체적인 객체로 작업 할 필요가 없습니다. 오히려 객체의 계약 수준에 머무를 수 있습니다. 또한, 이전 동기의 두 번째 부분과 달리, 당신이나 도구 작성자는 변형 된 객체를 혼합하는 상황을 결코 가질 수 없습니다. 여기서 프레임 워크 코드는 계약 수준으로 유지되지만 모든 도구 작성자는 자체 사례의 특성에 따라 자체 개체 사용이 제한됩니다. 이 경우 개체 생성은 각 구현 자에게 위임되며 프레임 워크 공급자는 개체를 만들고 반환하기위한 균일 한 메서드 만 제공합니다. 이러한 메소드는 프레임 워크 개발자가 코드를 진행하는 데 필연적이며 팩토리 메소드 ( 기본 패턴의 팩토리 메소드 패턴) 라는 특수한 이름이 있습니다.

몇 가지주의 사항 :

  • '템플릿 메소드'에 익숙하다면 어떤 프레임 워크 형태에 속하는 프로그램의 경우에도 템플릿 메소드에서 팩토리 메소드가 호출되는 것을 볼 수 있습니다. 반대로, 응용 프로그램의 템플릿 방법은 종종 특정 알고리즘의 단순 구현이며 공장 방법의 무효입니다.
  • 또한, 위에서 언급 한 프레임 워크를 사용하여 사고의 완성을 위해, 툴 빌더가 툴을 구축 할 때 각 팩토리 메소드 내에서 구체적인 객체를 생성하는 대신 책임을 추상적으로 위임 할 수 있습니다 도구 빌더는 향후 확장을 위해 구체적인 객체의 변형을 예견 할 수 있어야합니다.

샘플 코드 :

//Part of framework-code
BoardGame {
    Board createBoard() //factory method. Default implementation can be provided as well
    Piece createPiece() //factory method

    startGame(){        //template method
         Board borad = createBoard()
         Piece piece = createPiece()
         initState(board, piece)
    }
}


//Part of Tool-builder code
Ludo inherits  BoardGame {
     Board createBoard(){ //overriding of factory method
         //Option A: return new LudoBoard() //Lodu knows object creation
         //Option B: return LudoFactory.createBoard() //Lodu asks AbstractFacory
     }
….
}

//Part of Tool-builder code
Chess inherits  BoardGame {
    Board createBoard(){ //overriding of factory method
        //return a Chess board
    }
    ….
}



최소한의 인터페이스로 매우 간단하게 만들고 "// 1"에 집중하십시오.

class FactoryProgram
    {
        static void Main()
        {
            object myType = Program.MyFactory("byte");
            Console.WriteLine(myType.GetType().Name);

            myType = Program.MyFactory("float"); //3
            Console.WriteLine(myType.GetType().Name);

            Console.ReadKey();
        }

        static object MyFactory(string typeName)
        {
            object desiredType = null; //1
            switch (typeName)
            {
                case "byte": desiredType = new System.Byte(); break; //2
                case "long": desiredType = new System.Int64(); break;
                case "float": desiredType = new System.Single(); break;
                default: throw new System.NotImplementedException();
            }
            return desiredType;
        }
    }

여기서 중요한 점은 : 1. Factory & AbstractFactory 메커니즘은 상속 (System.Object-> byte, float ...)을 사용해야합니다. 그래서 만약 당신이 프로그램 상속을 가지고 있다면, Factory (Abstract Factory는 아마도 거기에 없을 것입니다.)는 이미 디자인에 의해 존재합니다. 2. Creator (MyFactory)는 concrete 타입을 알고 있으므로, caller (Main)에게 concrete 타입 객체를 반환합니다. 추상적 인 팩토리 리턴 타입은 Interface이다.

interface IVehicle { string VehicleName { get; set; } }
interface IVehicleFactory
    {
        IVehicle CreateSingleVehicle(string vehicleType);
    }
class HondaFactory : IVehicleFactory
    {
        public IVehicle CreateSingleVehicle(string vehicleType)
        {
            switch (vehicleType)
            {
                case "Sports": return new SportsBike();
                case "Regular":return new RegularBike();
                default: throw new ApplicationException(string.Format("Vehicle '{0}' cannot be created", vehicleType));
            }
        }
    }
class HeroFactory : IVehicleFactory
    {
        public IVehicle CreateSingleVehicle(string vehicleType)
        {
            switch (vehicleType)
            {
                case "Sports":  return new SportsBike();
                case "Scooty": return new Scooty();
                case "DarkHorse":return new DarkHorseBike();
                default: throw new ApplicationException(string.Format("Vehicle '{0}' cannot be created", vehicleType));
            }
        }
    }

class RegularBike : IVehicle { public string VehicleName { get { return "Regular Bike- Name"; } set { VehicleName = value; } } }
class SportsBike : IVehicle { public string VehicleName { get { return "Sports Bike- Name"; } set { VehicleName = value; } } }
class RegularScooter : IVehicle { public string VehicleName { get { return "Regular Scooter- Name"; } set { VehicleName = value; } } }
class Scooty : IVehicle { public string VehicleName { get { return "Scooty- Name"; } set { VehicleName = value; } } }
class DarkHorseBike : IVehicle { public string VehicleName { get { return "DarkHorse Bike- Name"; } set { VehicleName = value; } } }

class Program
{
    static void Main(string[] args)
    {
        IVehicleFactory honda = new HondaFactory(); //1
        RegularBike hondaRegularBike = (RegularBike)honda.CreateSingleVehicle("Regular"); //2
        SportsBike hondaSportsBike = (SportsBike)honda.CreateSingleVehicle("Sports");
        Console.WriteLine("******* Honda **********"+hondaRegularBike.VehicleName+ hondaSportsBike.VehicleName);

        IVehicleFactory hero = new HeroFactory();
        DarkHorseBike heroDarkHorseBike = (DarkHorseBike)hero.CreateSingleVehicle("DarkHorse");
        SportsBike heroSportsBike = (SportsBike)hero.CreateSingleVehicle("Sports");
        Scooty heroScooty = (Scooty)hero.CreateSingleVehicle("Scooty");
        Console.WriteLine("******* Hero **********"+heroDarkHorseBike.VehicleName + heroScooty.VehicleName+ heroSportsBike.VehicleName);

        Console.ReadKey();
    }
}

중요한 점 : 1. 요구 사항 : Honda는 "Regular", "Sports"를 만들 겠지만 Hero는 "DarkHorse", "Sports"및 "Scooty"를 만들 것입니다. 2. 왜 두 인터페이스? 하나는 제조업체 유형 (IVehicleFactory)이고 다른 하나는 제품 공장 (IVehicle) 용입니다. 2 개의 인터페이스를 이해하는 다른 방법은 추상적 인 팩토리는 관련된 객체를 만드는 것입니다. 2. catch는 IVehicleFactory의 자식 반환 및 IVehicle (공장의 콘크리트 대신)입니다. 그래서 부모 변수 (IVehicle)를 얻습니다. CreateSingleVehicle을 호출 한 다음 실제 자식 개체에 부모 개체를 캐스팅하여 실제 구체적인 형식을 만듭니다. RegularBike heroRegularBike = (RegularBike)hero.CreateSingleVehicle("Regular"); ; 당신은 ApplicationException을 얻을 것이고, 그래서 우리는 필연적으로 설명 할 일반 추상 팩토리가 필요합니다. 희망은 초급에서 중급의 시청자에게 도움이되기를 바랍니다.




언제든지 Factory Methods를 통해 Abstract Factory를 선호 할 것입니다. 위의 Tom Dalling의 예제 (훌륭한 설명 btw)에서 우리는 추상 팩토리가 더 복잡하다는 것을 알 수 있습니다. 우리가해야 할 일은 생성자에 다른 팩토리를 전달하는 것입니다 (여기에서 사용되는 생성자 종속성 주입). 그러나 Factory Method는 우리가 새로운 클래스 (더 많은 것들을 관리)를 소개하고 서브 클래 싱을 사용해야한다고 요구합니다. 상속보다 컴포지션을 항상 선호합니다.




위의 답변 중 많은 부분은 추상 팩토리와 팩토리 메서드 패턴 간의 코드 비교를 제공하지 않습니다. 다음은 Java를 통해 설명하려고 시도한 것입니다. 그것이 간단한 설명이 필요한 사람을 도울 수 있기를 바랍니다.

GoF가 적절하게 말한 것처럼 : Abstract Factory는 구체적인 클래스를 지정하지 않고 관련 객체 또는 종속 객체의 패밀리를 생성하기위한 인터페이스를 제공합니다.

        public class Client {
            public static void main(String[] args) {
               ZooFactory zooFactory = new HerbivoreZooFactory();       
               Animal animal1 = zooFactory.animal1();
               Animal animal2 = zooFactory.animal2();
               animal1.sound();
               animal2.sound();

               System.out.println();

               AnimalFactory animalFactory = new CowAnimalFactory();
               Animal animal = animalFactory.createAnimal();
               animal.sound();
            }
        }

        public interface Animal {
            public void sound();
        }

        public class Cow implements Animal {

            @Override
            public void sound() {
                System.out.println("Cow moos");
            }

        }

        public class Deer implements Animal {

            @Override
            public void sound() {
                System.out.println("Deer grunts");
            }

        }

        public class Hyena implements Animal {

            @Override
            public void sound() {
                System.out.println("Hyena.java");
            }

        }

        public class Lion implements Animal {

            @Override
            public void sound() {
                System.out.println("Lion roars");
            }

        }

        public interface ZooFactory {
            Animal animal1();

            Animal animal2();
        }

        public class CarnivoreZooFactory implements ZooFactory {

            @Override
            public Animal animal1() {
                return new Lion();
            }

            @Override
            public Animal animal2() {
                return new Hyena();
            }

        }

        public class HerbivoreZooFactory implements ZooFactory{

            @Override
            public Animal animal1() {
                return new Cow();
            }

            @Override
            public Animal animal2() {
                return new Deer();
            }

        }

        public interface AnimalFactory {
            public Animal createAnimal();
        }

        public class CowAnimalFactory implements AnimalFactory{

            @Override
            public Animal createAnimal() {
                return new Cow();
            }

        }

        public class DeerAnimalFactory implements AnimalFactory{

            @Override
            public Animal createAnimal() {
                return new Deer();
            }

        }

        public class HyenaAnimalFactory implements AnimalFactory{

            @Override
            public Animal createAnimal() {
                return new Hyena();
            }

        }

        public class LionAnimalFactory implements AnimalFactory{

            @Override
            public Animal createAnimal() {
                return new Lion();
            }

        }



Related