工厂模式 - 简单工厂模式






4.88/5 (50投票s)
在本系列文章中,我们将学习不同的工厂设计模式。工厂设计模式有三种,即简单工厂模式、工厂方法模式和抽象工厂模式。
引言
在本系列文章中,我们将学习不同的工厂设计模式。工厂设计模式有三种,即简单工厂模式、工厂方法模式和抽象工厂模式。我们将通过学习如何实现、何时实现来详细理解这三种模式,之后我们将理解它们之间的区别。简单工厂模式不是四人帮(GoF)书籍的一部分,但工厂方法模式和抽象工厂模式是该标准书籍的一部分。
为了使文章篇幅合理,本系列文章分为三部分。
目录
第一部分 - 简单工厂模式
- 什么是设计模式
- 设计模式如何帮助我们
- 简单工厂模式
- 没有简单工厂模式的问题
- 使用简单工厂模式解决
- 简单工厂模式的问题
第二部分 - 工厂方法模式
- 什么是工厂方法模式
- 通过示例理解定义
- 何时使用工厂方法模式
- 工厂方法模式的优点
第三部分 - 抽象工厂模式
- 什么是抽象工厂模式
- 何时使用抽象工厂模式
- 通过示例理解定义
- 抽象工厂模式的问题
什么是设计模式
正如维基百科所说,“设计模式是在软件设计中,给定上下文中,一个经常发生问题的通用可重用解决方案”。
换句话说,我们可以说设计模式为解决软件(通常是大型)应用程序中经常发生的问题提供了经过验证的解决方案。在任何应用程序中实现特定的设计模式都会提高应用程序的灵活性、可维护性和可读性。在应用程序中实现任何设计模式之前,我们首先应该清楚某个特定设计模式可以解决的问题。
设计模式如何帮助我们
应用程序可以在不实现设计模式的情况下开发。如果不使用设计模式,最初的应用程序开发时间会更短,但随着应用程序功能的增长,您会发现更改、维护或理解设计非常困难。这就是设计模式帮助我们识别通用问题并以最佳方式解决它的地方。
处理设计模式时,首先也是最重要的一部分是理解应用程序的上下文和确切问题。一旦确定了问题,就可以轻松地找出应该使用哪种设计模式来解决已确定的问题。
什么是简单工厂模式
根据维基百科的定义,“工厂是用于创建其他对象的对象”。简单工厂模式是其最简单的形式的工厂
类(与工厂方法模式或抽象工厂模式相比)。换句话说,我们可以说: 在简单工厂模式中,我们有一个工厂类,该类有一个方法,该方法根据给定的输入返回不同类型的对象。
让我们通过一个例子来理解
示例概述
为了在实践中理解简单工厂模式,我们以一家生产各种风扇的电气公司为例,我们将其称为FanFactory
。但是,首先,我们将不使用简单工厂模式来实现此场景,然后查看问题以及如何用该模式解决这些问题。
下面的程序是一个简单的控制台应用程序,在该程序中,Main
方法用作客户端,它创建了一个TableFan
。在最简单的实现中,我们可以看到客户端可以根据需要直接创建TableFan
(没有任何工厂)。
class Program
{
static void Main(string[] args)
{
TableFan fan = new TableFan();
fan.SwitchOn();
}
}
class TableFan { }
没有简单工厂模式的问题
在上面的示例中,我们没有使用任何模式,应用程序运行正常。但是,如果我们考虑未来可能的变化并仔细观察,我们可以预见到当前实现的以下问题:
- 在当前应用程序中, wherever a particular fan is needed, it is being created using concrete class. In future, if there is any change is class name/ different concrete class is proposed, you have to make change all over the application. For example, instead of having
TableFan
class, we need to introduceBasicTableFan
(which should be used now in place of oldTableFan
class) andPowerTableFan
classes. So any changes with respect toFan
classes will make it difficult to maintain the code and requires many changes at many places.
- Currently, when client creates
TableFan
object, constructor ofTableFan
class is not taking any argument soFan
creation process is easy. But laterTableFan
object creation process gets changed and what if constructor ofFan
class is expecting two objects as arguments. Then every place in code client needs to make changes whereverFan
object was created. For exampleTableFan fan = new TableFan(Moter moter, BladType bladType);
- In
TableFan fan = new TableFan(Moter moter, BladType bladType);
code whenFan
object is created, it takes two typesMoter
,BladType
. In the above code, the client is aware about classes and knows the object creation process as well, which should be internal detail ofFanFactory
. We should avoid exposing such object creation detail and internal classes to client application.
- In certain cases, the lifetime management of the created objects must be centralized to ensure a consistent behavior within the application. That cannot be done if client is free to create concrete object the way he wishes. Such scenario often occurs in Cache Management and DI frameworks.
- In C# and Java, the constructor of a class must have the same name as the name of that class. Let’s say there is a need to have descriptive names for constructor like
CreateTableFanByModelNumber(int modelNumber)
. InTableFan
class, at best, we can have a constructor likeFan(int modelNumber)
but the name is not as descriptive as its intent. Another similar example on the issue of descriptive name is available at this wikidedia page too.
使用简单工厂模式解决
To come out with the above problems, we can use Simple Factory Pattern because this pattern is suitable to solve the above mentioned problems. Further, we will continue with the same example and will modify the existing code.
When we implement Simple Factory Pattern, a class needs to be created which will have method to return requested instance of an object. Let's create a class called "FanFactory
" which will implement an interface called "IFanFactory
". This interface has a method called IFan CreateFan(FanType type);
which takes enum FanType
and returns respective instance of a Fan
.
Now Client
will not be aware about the concrete classes like TableFan
or CeilingFan
. Client will be using FanType enum
and IFan interface
. Based on passed enum
as argument while calling "CreateFan
" method, FanFactory
will return the instance of desired fan. Following is the modified code of application
enum FanType
{
TableFan,
CeilingFan,
ExhaustFan
}
interface IFan
{
void SwitchOn();
void SwitchOff();
}
class TableFan : IFan {.... }
class CeilingFan : IFan {.... }
class ExhaustFan : IFan {..... }
interface IFanFactory
{
IFan CreateFan(FanType type);
}
class FanFactory : IFanFactory
{
public IFan CreateFan(FanType type)
{
switch (type)
{
case FanType.TableFan:
return new TableFan();
case FanType.CeilingFan:
return new CeilingFan();
case FanType.ExhaustFan:
return new ExhaustFan();
default:
return new TableFan();
}
}
}
//The client code is as follows:
static void Main(string[] args)
{
IFanFactory simpleFactory = new FanFactory();
// Creation of a Fan using Simple Factory
IFan fan = simpleFactory.CreateFan(FanType.TableFan);
// Use created object
fan.SwitchOn();
Console.ReadLine();
}
Now we will confirm how Simple Factory Pattern solved all the above mentioned problems under "Problem without implementation Simple Factory Pattern" section
- Now client is using interfaces and
FanFactory
, so if we change the name of concrete class ofFan
which gets created for a givenenum
value, we need to change at only one place, that is, inside "CreateFan
" method ofFanFactory
. Client code is not affected at all.
- If later, the
Fan
object creation process gets changed and constructor ofFan
class is expecting two or more objects as arguments, we need to change only inside "CreateFan
" method ofFanFactory
. Client code is not affected at all.
- Using
FanFactory
, all internal details will be hidden from client. So it is good in terms of abstractions and security.
- If needed, we can write logic for lifetime management along with objects creation at centralized
FanFactory
.
- While using
FanFactory
we can simply have methods with different and more descriptive names which would return an object ofIFan
. In our example application,FanFactory
can have a public methodCreateTableFanByModelNumber(int modelNumber)
exposed to client.
简单工厂模式的问题
Let’s say in future, if FanFactory
has to make a new type of fan called WallFan
also. To adopt this new requirement, we have to change CreateFan
method and add one switch
case for WallFan
type. If again new kind of Fan
is introduced, then again one more case needs to be added. This will be a violation of Open Close Principle of SOLID principles. In the next article, we will see how we can overcome this violation issue with the help of Factory Method Pattern.
结论
In this article, we had a walkthrough to learn Simple Factory Pattern and its use. We understood the context of Simple Factory Pattern and how to use it to enhance maintainability of application. But it violates the Open Close Principle, so in the next article , we will see how we can have better design with Factory Method Pattern. Thanks for reading! Your comments and suggestions for improvement are most welcome.