I get the idea of the factory pattern, but I feel that it is really not necessary to use this pattern.

For example, below is some code I saw (C#) that use factory method:

public interface IAnimal
{
   void Speak();
}

public class Dog : IAnimal
{
   public void Speak()
   {
      Console.WriteLine("Dog says: Bow-Wow.");
   }
}

public class Tiger : IAnimal
{
   public void Speak()
   {
      Console.WriteLine("Tiger says: Halum.");
   }
}

public abstract class IAnimalFactory
{
   public abstract IAnimal CreateAnimal();
}

public class TigerFactory : IAnimalFactory
{
   public override IAnimal CreateAnimal()
   {
      return new Tiger();
   }
}

public class DogFactory : IAnimalFactory
{
   public override IAnimal CreateAnimal()
   {
      return new Dog();
   }
}

and client can invoke:

IAnimalFactory tigerFactory = new TigerFactory();
IAnimal aTiger = tigerFactory.MakeAnimal();
aTiger.Speak();  //3 lines of code, plus needing of extra factory classes

but Client can also do like:

IAnimal aTiger = new Tiger();
aTiger.Speak();  //only 2 lines of code

we can see that only 2 lines of code is needed, and we don't need to define factory classes. so why takes extra steps to define and use factories?

Ant P replied that RandomNumberOfAnimalsGenerator needs a factory, but below is my version of the class, still doesn't need any factory.

public class RandomNumberOfAnimalsGenerator
{
    private readonly animal ;

    public RandomNumberOfAnimalsGenerator(IAnimal animal)
    {
        this.animal = animal;
    }

    public List<IAnimal> GetAnimals()
    {
        var animals = new List<IAnimal>();
        var n = RandomNumber();

        for(int i=0; i<n; i++)
        {
            animals.Add(animal);
        }

        return animals;
    }
}

and client invokes:

var RandomNumberOfAnimalsGenerator = new RandomNumberOfAnimalsGenerator(new Tiger());

still doesn't need a factory

有帮助吗?

解决方案

I'm not sure yet another answer is needed here, but you asked for your code to be modified to explain the purpose of factories. So perhaps the following will help.

Firstly, I'm going to change your IAnimalFactory to be an interface, rather than an abstract class. It serves no useful purpose being the latter. Also, we likely will have some criteria around what sort of animal we want for a specific circumstance. So let's have an enum that defines that criteria:

public enum AnimalType
{
    Pet,
    Wild
}

Then we can create our factory interface:

public interface IAnimalFactory
{
    IAnimal CreateAnimal(AnimalType typeofAnimal);
}

So now we have a means of letting other parts of the code create an animal, without having to know anything about what animals exist. We simply get to specify whether we want a wild one or not:

public void CreateAWildAnimalAndMakeItTalk(IAnimalFactory factory)
{
    var animal = factory.CreateAnimal(AnimalType.Wild);
    animal.Speak();
}

The only thing left to do is create an actual factory. Let's keep that simple:

internal class SimpleAnimalFactory : IAnimalFactory 
{
    public IAnimal CreateAnimal(AnimalType typeofAnimal)
    {
       return typeofAnimal == AnimalType.Wild ? new Tiger() : new Dog();
    }
}

And then it gets used elsewhere in the code:

CreateAWildAnimalAndMakeItTalk(new SimpleAnimalFactory());

But dogs can be wild too, so we might want to offer a different implementation of the factory:

internal class RandomWildAnimalFactory : IAnimalFactory 
{
    private Random _random = new Random();

    public IAnimal CreateAnimal(AnimalType typeofAnimal)
    {
        if (typeofAnimal == AnimalType.Pet) return new Dog();

        return _random.Next(2) == 0 ? new Tiger() : new Dog();
    }
}

Then we can call our method with the new factory, without having to change any other code:

CreateAWildAnimalAndMakeItTalk(new RandomWildAnimalFactory());

其他提示

Factory methods have a number of advantages. Mainly, they avoid the inbuilt limitations of constructors (can only have one name, cannot use caching, etc.).

Entire Factories are used mainly to decouple the client code from the concrete types used to instantiate the interface. But your example doesn't do this. Your client doesn't have to know the Tiger type, but instead it has to know the TigerFactory type, which is no improvement.

In my view, this is cargo cult programming - someone was told to use a factory and obeyed without understanding what the point of factories is. The proper thing to do would have been to have an AnimalFactory that can instantiate both tigers and dogs - but only if it can determine which to provide when. Otherwise, the decision about the concrete type of animal to use must stay with the client.

There are different types of factories that serve different use cases. Often, you want to create objects of different (sub-) classes depending on runtime arguments. For this case, a simple function or a non-polymorphic class can be sufficient:

public class AnimalFactory
{
    public static IAnimal Create(String typeName)
    {
        if (typeName == "Tiger")
        {
            return new Tiger();
        }
        if (typeName == "Dog")
        {
            return new Dog();
        }
        // handle error, for example:
        throw Exception("Unkown type name: " + typeName);
    }
}

This factory allows you, for example, to create different animals depending on user input:

String userInput = Console.ReadLine();
IAnimal animal = AnimalFactory.Create(userInput);
animal.Speak();

As you already noticed, there is no need for a base class / interface IAnimalFactory with different factory subclasses. Here, those would only complicate the factory usage.

With above factory, whenever calling Create, you must provide the argument userInput that allows the type selection. However, in some cases, you want to separate the type selection from the actual creation. Take a look at the following example:

public class AnimalStore
{
    IAnimalFactory m_animalFactory;
    double m_price;
    public AnimalStore(IAnimalFactory animalFactory, double price)
    {
        this.m_animalFactory = animalFactory;
        this.m_price = price;
    }
    public void SellAnimalToUser(User user)
    {
        if (user.money < this.m_price)
        {
            throw Exception("Not enough money");
        }
        user.money -= this.m_price;
        user.AddAnimal(m_animalFactory.CreateAnimal());
    }
}

The AnimalStore is not polymorphic. However, different stores can still create different animals. When calling SellAnimalToUser (which calls Create), you dont need any argument that selects the correct animal type.

AnimalStore tigerStore = new AnimalStore(new TigerFactory(), 1000.);
AnimalStore dogStore = new AnimalStore(new DogFactory(), 200.);
User peter = new User();
peter.money = 5000;
tigerStore.SellAnimalToUser(peter);
dogStore.SellAnimalToUser(peter);

The problem of separating type selection from object creation can also be solved by other means. For example, you could have a non-polymorphic AnimalFactory that lets you specify the String typeName in its constructor instead of its Create function. Or you could inject functors instead of factories. These methods have their own advantages and disadvantages and which you want to use usually depends on how you want to use it.

The benefit of the Factory Method pattern is in expressing the fact that a piece of code does not want/need to know how to obtain an IAnimal.

In fact, interfaces are often viewed from the wrong perspective: you can never force client code to use your interface.

Rather, the client code can specify that it requires an interface, in this case, "a way to create animals".

It would not be so dumb that the client code even defined the interface itself, expressing that: "you take care of creation, I'll put them in a zoo".

Note that this pattern was defined in an era where programming languages had no lambda expressions. In today's context, many of the patterns have become obsolete. The Factory Method can now be implemented with a simple Func<IAnimal>:

public void myCode(Func<IAnimal> createAnimal) {
   var a = createAnimal();
   ... // stuff with a
}

You asked for an example of why you'd ever need a factory but you haven't really been given one yet.

Suppose you have a piece of code that creates a random number of animals. How do you decouple this from the details of how to create a specific type of animal?

The answer: a factory.

public class RandomNumberOfAnimalsGenerator
{
    private readonly Func<IAnimal> createAnimal;

    public RandomNumberOfAnimalsGenerator(Func<IAnimal> createAnimal)
    {
        this.createAnimal = createAnimal;
    }

    public List<IAnimal> GetAnimals()
    {
        var animals = new List<IAnimal>();
        var n = RandomNumber();

        for(int i=0; i<n; i++)
        {
            animals.Add(createAnimal());
        }

        return animals;
    }
}

Without this factory, RandomNumberOfAnimalsGenerator needs to know how to create every type of animal.

One use I haven't seen in these answers that's really common but you are kind of looking at the problem backwards (Your example code is both consuming and creating the factory, which isn't generally useful). A good use for it is where there are 2+ sets of code interacting (maintained by completely different teams).

Let's take something like a log library as an example... This might be a utility class that you use to log out to a file.

Suppose the log has quite a few destinations, File, database, console, … that you can configure through either configuration commands or a config file, but what if you want to log to some other device--a remote port, or some strange old paper tape punch you found in your closet? On top of that you want any other libraries that you are using that might use the same logging software to use YOUR special logging back-end.

In other words, how can you supply the logger with code it should run when it logs?

I can see 3 ways:

  1. add your class name to the logging library's config file
  2. Provide the logger with a concrete instance of your logging back-end
  3. provide a factory to the logging library that it can use to create it's own instances of your logging back-end.

2 and 3 are close--the main difference is that if the library needs a new logging backend each time the client code creates a logger, then solution 2 wouldn't work and you'd need a factory.

Looking at it from the user's point of view you hardly even see that it uses an abstract factory, but when you look at it as an implementer of the logging library, it seems quite necessary.

Your code is not an example of factory method. Factory method is just one single method, it is usually a static (in case of java). Having abstract method which is overloaded in different derivations doesn't make sense because in that case you need to create real single factory method to instantiate entities of implementation.

I suppose your code is part of Factory pattern (not FactoryMethod) which is different. Factory includes factory method as a starting point to create itself. Moreover IFactory can (and should to be useful) contain multiple methods for each interface in a set of types involved in process of multiple implementation. For example.

public abstract class IAnimalFactory {
    public abstract IAnimal CreateAnimal();
    public abstract ILocation CreateLocation();
    public abstract IFood CreateFood();
}

class Zoo extends ILocation;
class Savannah extends ILocation;

IAnimal.placeTo(ILocation);

And so on.

Your RandomNumberOfAnimalsGenerator doesn't make a random number of animals. It makes precisely zero animals, in fact. What it does do is create a new list, containing several references to the same animal.

For a RandomNumberOfAnimalsGenerator, I should be able to pass it "dog" and get back a list: Fido, Spot, Max.

If I then feed the dogs: foreach(IAnimal a in randomlyGeneratedAnimals) {a.Feed();} then I feed three dogs once.

With your RandomNumberOfAnimalsGenerator, I can pass it "Fido" and get back a list: Fido, Fido, Fido.

If I then feed the dogs: foreach(IAnimal a in randomlyGeneratedAnimals) {a.Feed();} then I feed Fido three times.

In your program, I can't tell the difference, because all dogs are identical, because they don't have any fields. If I add a field:

public interface IAnimal
{
   void Speak();
   void Feed();
}

public class Dog : IAnimal
{
   public void Speak()
   {
      Console.WriteLine("Dog says: Bow-Wow.");
   }
   private int Hunger = 5;
   public void Feed()
   {
      Hunger--;
      Console.WriteLine("Hunger level is "+Hunger);
   }
}

then you will get different results, if you have the same dog three times instead of having three different dogs.

许可以下: CC-BY-SA归因
scroll top