Role of Creator in Factory Pattern

爷,独闯天下 提交于 2019-12-11 06:28:36

问题


I couldn't understand the role of defining an abstract class / interface for the factory class, which is something i always see in all tutorials on the web. Can somebody please put some light on the importance of CreatorInterface ? Reference UML Diagram of the Factory Pattern

To put in code form, here's what i have :

Code Example 1

// Product
public abstract class Vehicle
{
     public string VehicleType { get; set; }
}

// Concrete Product
public class Bike : Vehicle
{
    public Bike()
    {
        VehicleType = "Two Wheeler";
    }
}

// Concrete Product
public class Car : Vehicle
{
    public Car()
    {
        VehicleType = "Four Wheeler";
    }
}

// Concrete Factory
public class VehicleFactory
{
     public Vehicle GetVehicle(string VehicleType)
    {
        if (VehicleType == "Bike")
            return new Bike();
        else if (VehicleType == "Car")
            return new Car();
        else
            return null;
    }
}

// Client class
public class ClientClass
{
    public void Main()
    {
        VehicleFactory VehicleFactoryObj = new VehicleFactory();
        Vehicle BikeObj = VehicleFactoryObj.GetVehicle("Bike");
        Vehicle CarObj = VehicleFactoryObj.GetVehicle("Car");
    }
}

The above code doesn't contain any abstract class for the 'VehicleFactory' class. But it works fine. Now, what can be the reasons for adding an abstract class for the 'VehicleFactory' ? In my view adding an abstract class will make sense for the Abstract Factory method. [Please correct me if i'm wrong]

Update : Adding my understanding of the Factory Pattern so far.

Definition by GoF :

Define an interface for creating an object, but let subclasses decide which class to instantiate. The Factory method lets a class defer instantiation it uses to subclasses.

As far as i could understand, the core problem statement behind the pattern is that you want to create instances of different classes, without exposing the creation logic to the consumer. Please let me know if i'm getting anything wrong here. I am a little confused here too, because of the examples i see on the web. For e.g on Wiki, the Php and C# examples. I could digest the requirement of the pattern in C# example, but not in PHP example. Anyways, below statements will help you to understand my question clearly.

For instance, we have two vehicle classes Bike and Car in our library, and both of them has vehicle model number. The Bike model number starts with "BK" and car model numbers start with "CR". Now, we wish to return instance of either of the classes depending of the Vehicle Model Number, without exposing the logic to the client. [Note This is an updated scenario, i am putting up since the earlier one had a weak logic of deciding the class, and created confusion over the use of string]

So we can create a vehicle factory class, which exposes a static method that returns the appropriate vehicle instance.

If selection logic is to be known to the client, then i might not have needed the pattern itself. So, the could would look like :

Code Example 2

// Product
public abstract class Vehicle
{
     public int NumberOfWheels { get; set; }
}

// Concrete Product
public class Bike : Vehicle
{
    public Bike()
    {
        NumberOfWheels = 2;
    }
}

// Concrete Product
public class Car : Vehicle
{
    public Car()
    {
        NumberOfWheels = 4;
    }
}

// Client class
public class ClientClass
{
    public void Main()
    {
        String ModelNumber = "BK-125";

        Vehicle CurrentVehicle;
        if (ModelNumber.Contains("BK"))
        {
            CurrentVehicle = new Bike();
        }
        else if(ModelNumber.Contains("CR"))
        {
            CurrentVehicle = new Car();
        }
    }
}

The Factory Pattern let's me simply hide the creational logic from the client by creating a factory. Thus the Client now just needs to call the Factory's create method, and he'll get the appropriate class instance in return. Now the code will look like.

Code Example 3

// Product
public abstract class Vehicle
{
     public int NumberOfWheels { get; set; }
}

// Concrete Product
public class Bike : Vehicle
{
    public Bike()
    {
        NumberOfWheels = 2;
    }
}

// Concrete Product
public class Car : Vehicle
{
    public Car()
    {
        NumberOfWheels = 4;
    }
}

// Concrete Factory
public class VehicleFactory
{
     public Vehicle GetVehicle(string ModelNumber)
    {
        if (ModelNumber.Contains("BK"))
            return new Bike();
        else if (ModelNumber.Contains("CR"))
            return new Car();
        else
            return null;
    }
}

// Client class
public class ClientClass
{
    public void Main()
    {
        VehicleFactory VehicleFactoryObj = new VehicleFactory();
        Vehicle BikeObj = VehicleFactoryObj.GetVehicle("BK-125");
        Vehicle CarObj = VehicleFactoryObj.GetVehicle("CR-394");
    }
}

Now the question comes about the abstract factory class
One benefit of adding an abstract factory class, which i understood from the discussion is that the Client will then be able to override the 'GetVehicle' method to override the logic. For a case where he might have created more vehicle classes for example a 'Truck'. But even in this case, if he want's to override the factory method for all three namely Bike, Car and Truck, he will not be have the entire logic with him as the logic for Bike and Car creation is written in Factory method. Although he will be able to create a new logic for all his new vehicle types. Can someone please put some light on this ?

I more point i want to make here is that This question is regarding the Factory Pattern, i do understand that Abstract Factory pattern will require an Abstract Factory, since in Abstract Factory pattern we're creating Factory of Factories. But in Factory pattern we just have a factory of objects, then why do we need an interface for a factory ?

Thanks in advance !! :-)


回答1:


@Yair Halberstadt "...Why should i prefer the Factory method over the Static Factory ...". Both serve different purpose. Static factory is a code block which (just) collects the instantiation code for an object hierarchy (it is easy to get convinced about the name). But its nature is static (though can be written at instance level) meaning it gets bound at compile time. off course we can extend static factory but mainly as a patch code. (mostly not preferred at framework level for clients to extend.)

In contrast Factory method is useful for writing common business logic without specifying the concrete beneficiary types which clients will write on later dates.

A fictitious example (with assumption of universal Tax computation)-

public abstract class TaxComputer {
    protected abstract Calculator getCalculator();// Factory method
    public void registerIncome(){
        //...
    }
    public Amount computeTax(){
        Calculator alculator = getCalculator();
        //... Tax computation logic using abstract Calculator
        // Note: Real Calculator logic has not been written yet
        return tax;
    }
}

public interface Calculator {
    Amount add(Amount a, Amount b);
    Amount subtract(Amount a, Amount b);
    Amount multiply(Amount a, double b);
    Amount roundoff(int nearestAmount);
    // ...
}

My all Tax rule implementations are referring to abstract Calculator for operations using Amount. It requires only abstract Calculator at this time. Once the Tax computer is ready, It can be published for clients to extend (With extension point at abstract classes and hook factory method).

Specific client can extend it to computations for Yen (Does not have decimal point) or Dollar/ Pound etc or even calculators which round off (e.g. to next 10 Rupees) after every operation as per local rule.

Same way USDollarTaxCalculator will extend it with their own rules for operations (but can not and need not redefine Tax rules)

public class YenTaxComputer extends TaxComputer {
    @Override
    protected Calculator getCalculator() {
        return new YenCalculator();
    }
}

public class YenCalculator implements Calculator {
    @Override
    public Amount add(Amount a, Amount b) {
        /*Yen specific additions and rounding off*/ 
    }
    @Override
    public Amount subtract(Amount a, Amount b) {/*...*/ }
    @Override
    public Amount multiply(Amount a, double b) {/*...*/ }
    @Override
    public Amount roundoff(int nearestAmount) {/*...*/  }
}

In factory method point is not about hiding creation logic but keeping extensions possible for clients.

The client will look something like

    public class TaxClient {
        public static void main(String[] args) {
            TaxComputer computer = new YenTaxComputer();//
            computeTax(computer);
        }
        /** A PLACE HOLDER METHOD FOR DEMO
         */
        private static void computeTax(TaxComputer computer) {
            Amount tax = computer.computeTax();
            Amount Payment = ...
            //...
        }
    }

Points to be noted here

  1. Clients do not use factory method getCalculator()
  2. Concrete Instance of Creator/ factory can be created by static factories/ reflection etc. choice can be made from system variables, platform, locale etc.
  3. This design pattern comes with its own drawbacks/ costs like parallel class hierarchies (YenTaxComputer has only responsibility of creating instance). nothing comes free.



回答2:


Simply speaking, your base classes should be abstract or not depending on whether you want to be able to declare a New one.

If you want to prevent the a developer (or yourself) from ever going:

var myVehicle = new Vehicle();

And instead enforce that all Vehicles should be the child ones that inherit from Vehicle, then you declare it abstract.

Then when they try and do the above code they get a compilation error that they cannot declare a new abstract class.

There is nothing wrong having the base class non abstract if you are fine with users being able to declare new ones.




回答3:


Yes it works, but it's not ideal. VehicleFactory has a very general method, and will be a pain in the neck to use once you add more vehicles, as you'll need an extremely long method to check for all the strings.

Imagine you had Fifteen vehicles. Then you'd need an extremely long method to enumerate through all the options and generate the right car. This is both unnecessarily slow, and will be prone to error as you could easily miss something out/delete it, and it will be very hard to debug. In general long methods are bad code smell.

Besides this necessitates that you edit the VehicleFactory class every time you add something that inherits Vehicle. But what happens if a user of your library doesn't have access to it but wants to inherit from vehicle? However by defining an abstract VehicleFactory class he can inherit from that and define his own factory method.

In short the abstract factory method simply makes your code far easier to scale.

Also generating a vehicle dependent on a string is a very bad idea; what if you use an uppercase, or misspell? Besides for which it's quite slow. Better to have something like this.

public abstract class VehicleFactory
{
     public abstract Vehicle GetVehicle(string VehicleType)
}

public class CarFactory : VehicleFactory
{
    public override Vehicle GetVehicle(string VehicleType)
    {
          return new Car();
    }
}

public class BikeFactory : VehicleFactory
{
    public override Vehicle GetVehicle(string VehicleType)
    {
          return new Bike();
    }
}

public class ClientClass
{
    public void Main()
    {
        //Create Factories
        BikeFactory BikeFactoryObj = new BikeFactory();
        CarFactory CarFactoryObj = new CarFactory();

        //create Vehicles from factories. If wanted they can be casted to the inherited type.
        Vehicle VehicleObj=BikeFactoryObj.GetNewVehicle();
        Bike BikeObj = (Bike)BikeFactoryObj.GetVehicle();
        Car CarObj = (Car)CarFactoryObj.GetVehicle();

        //They are all inherited from Vehicle so can be used in a list of Vehicles
        List<Vehicle> Vehicles=new List<Vehicle>()
        {
             VehicleObj,
             BikeObj,
             CarObj
        }
    }
}

There's far less opportunity here for error, and this can be easily scaled by any user of the class.




回答4:


I found the following link very helpful to understand the logic behind having an abstract class for Factory, and the logic of using Factory Pattern itself.

https://msdn.microsoft.com/en-us/library/ee817667.aspx

Let me quote the logic behind the pattern from the article

Consider an application that models the assembly of a variety of personal computers. The application contains a ComputerAssembler class that is responsible for the assembly of the computer, a Computer class that models the computer being built, and a ComputerFactory class that creates instances of the Computer class. In using the Factory pattern, the ComputerAssembler class delegates responsibility for creation of Computer instances to the ComputerFactory. This ensures that if the instantiation and/or initialization process changes (e.g. new constructor, use of an activator, custom pooling, etc.), the ComputerAssembler does not need to change at all. This is the benefit of abstracting the creation of an object from its use.

In addition, suppose that the business requirements changed and a new type of computer needs to be assembled. Rather than modifying the ComputerAssembler class directly, the ComputerFactory class can be modified to create instances of a new computer class (assuming that this new class has the same interface as Computer). Furthermore, it is also possible to address this requirement by creating a new factory class (that has the same interface as ComputerFactory) that creates instances of the new computer class (again, with the same interface as Computer). As before, nothing in the ComputerAssembler class needs to change, all the logic just continues to work as it had previously. This is the benefit of abstracting the types of the product and factory into invariant interfaces.

The second paragraph states that the Creator enables us to create new factories, to handle the changes that might have been introduced in the Product, or in the logic of their creation, forcing us to create new factory.



来源:https://stackoverflow.com/questions/46433489/role-of-creator-in-factory-pattern

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!