。 工厂模式同样是项目中最常用的设计模式,工厂模式中又分为简单工厂,工厂方法,抽象工厂。下面我们由简单的开始逐一介绍。
1.简单工厂模式
简单工厂又被称为静态工厂,在设计模式中属于创建型模式。主要解决的问题是封装了实例化的过程,通过传入参数来获不同实例。下面我们举一个项目中可能会用到的例子。
假设我们程序的数据保存在几个不同的数据库中,有MySql,SQLServer和MongoDB。数据库都有增删改查的操作,我们就声明一个接口定义这些操作。
1 public interface IDatabase
2 {
3 void Insert();
4 void Delete();
5 }
然后我们让三个数据库类分别实现这个接口。
1 public class SqlServer : IDatabase
2 {
3 public void Delete()
4 {
5 Console.WriteLine("delete data from sqlserver");
6 }
7
8 public void Insert()
9 {
10 Console.WriteLine("insert data to sqlserver");
11 }
12 }
13
14 public class MySql : IDatabase
15 {
16 public void Delete()
17 {
18 Console.WriteLine("delete data from mysql");
19 }
20
21 public void Insert()
22 {
23 Console.WriteLine("insert data to mysql");
24 }
25 }
26
27 public class MongoDB : IDatabase
28 {
29 public void Delete()
30 {
31 Console.WriteLine("delete data from mongoDb");
32 }
33
34 public void Insert()
35 {
36 Console.WriteLine("insert data to mongoDb");
37 }
38 }
之后我们再声明一个工厂类,这个类中的静态方法可以根据传入不同的参数来创建不同的实例。
1 public static class DatabaseFactory
2 {
3 public static IDatabase CreateDatabase(string dbType)
4 {
5 IDatabase db = null;
6
7 switch (dbType)
8 {
9 case "MySql":
10 db = new MySql();
11 break;
12 case "SqlServer":
13 db = new SqlServer();
14 break;
15 case "MongoDB":
16 db = new MongoDB();
17 break;
18 default:
19 break;
20 }
21
22 return db;
23 }
24 }
最后我们再Main函数里声明三个接口,然后给工厂类传入不同的参数来创建三个不同的实例,再分别调用接口中声明的方法。
1 static void Main(string[] args)
2 {
3 IDatabase db1 = DatabaseFactory.CreateDatabase("MySql");
4 db1.Insert();
5 db1.Delete();
6
7 IDatabase db2 = DatabaseFactory.CreateDatabase("SqlServer");
8 db2.Insert();
9 db2.Delete();
10
11 IDatabase db3 = DatabaseFactory.CreateDatabase("MongoDB");
12 db3.Insert();
13 db3.Delete();
14 }
来看一些控制台的输出:
这就是简单工厂模式。
我们可以看到简单工厂模式的优点:
1.拓展性好,如果这时候我们又添加了一个Oracle数据库,只需要再添加一个新的类并实现IDatabase这个这个接口就行了。
2.我们只需要关注接口中的方法,不需要关注具体类的实现。
缺点:只适用于工厂需要创建比较少的具体类这样的情况。如果具体类多,代码的复杂程度会增加。
2.工厂模式
工厂模式在简单工厂模式的基础上进行了更加全面的面向对象封装,可以让我们不要单独的工厂方法就能创建出具体的实例。做法就是为每一个具体的类创建单独的工厂。接下来我们对刚刚几个类稍加改造。
首先发DatabaseFactory修改成一个接口,接口中定义一个用来创建实例的方法。
1 interface IDatabaseFactory
2 {
3 IDatabase CreateDatabase();
4 }
然后我们然后我们为每个具体类单独创建一个工厂类,工厂类实现刚刚定义的接口。
1 public class MongoDbFactory : IDatabaseFactory
2 {
3 public IDatabase CreateDatabase()
4 {
5 return new MongoDB();
6 }
7 }
8
9 public class MySqlFactory : IDatabaseFactory
10 {
11 public IDatabase CreateDatabase()
12 {
13 return new MySql();
14 }
15 }
16
17 public class SqlServerFactory : IDatabaseFactory
18 {
19 public IDatabase CreateDatabase()
20 {
21 return new SqlServer();
22 }
23 }
最后我们在main函数中创建工厂的实例。
static void Main(string[] args)
{
IDatabaseFactory dbFactory1 = new MySqlFactory();
IDatabase db1 = dbFactory1.CreateDatabase();
db1.Insert();
db1.Delete();
IDatabaseFactory dbFactory2 = new SqlServerFactory();
IDatabase db2 = dbFactory1.CreateDatabase();
db2.Insert();
db2.Delete();
IDatabaseFactory dbFactory3 = new MongoDbFactory();
IDatabase db3 = dbFactory3.CreateDatabase();
db3.Insert();
db3.Delete();
}
结果输出是和刚刚一模一样的。工厂模式的好处便是它符合开闭原则(对扩展开放,对修改封闭)。在刚刚的简单工厂模式中,如果我们扩展一个新的类,除了添加一个新的类之外,我们还需要去修改CrateDatabase(string dbType)这个方法,这是违反开闭原则的。在工厂模式中我们就不需要修改CreateDatabase这个方法,只需要实现工厂类这个接口便能完场扩展。缺点便是我们需要写更多的代码。
3.抽象工厂模式
有了前面工厂模式的铺垫,抽象工厂应该不难理解吧。我看到过很多的博客都写着很多概念,什么产品层级,产品族,抽象产品等等,感觉不是特别容易理解。我的理解是这样的:把多个不同的工厂再抽象出来,再用一个抽象工厂(超级工厂)来创建这些工厂。也就是说抽象工厂是工厂的工厂。为了说明这个模式我想出了一个例子(其实我在工作中没有遇到过使用抽象工厂的例子):
操作系统有Windows操作系统,Linux操作系统。每个操作系统都可以启动关闭。于是我们就创建一个操作系统工厂,用来创建(安装)这些操作系统,方法和上面的创建数据库工厂是一样的。
// 操作系统具有的操作
public interface IOpSystem
{
void Start();
void Shutdown();
}
// 操作系统工厂
public interface IOpSystemFactory
{
IOpSystem InstallSystem();
}
// windows操作系统
public class WindowsSystem : IOpSystem
{
public void Shutdown()
{
Console.WriteLine("windows shutdown");
}
public void Start()
{
Console.WriteLine("windows start");
}
}
// linux操作系统
public class LinuxSystem : IOpSystem
{
public void Shutdown()
{
Console.WriteLine("linux shutdown");
}
public void Start()
{
Console.WriteLine("linux start");
}
}
// windows操作系统工厂,用来创建windows系统实例
public class WindowsFactory : IOpSystemFactory
{
public IOpSystem InstallSystem()
{
return new WindowsSystem();
}
}
// linux操作系统工厂,用来创建linux系统实例
public class LinuxFactory : IOpSystemFactory
{
public IOpSystem InstallSystem()
{
return new LinuxSystem();
}
}
我们可以看到操作系统工厂和数据库工厂是完全两个不同的工厂。假设一台服务器上需要安装操作系统和数据库,我们便可以用一个超级工厂来把这两个不同的工厂抽象出来。
public interface ISuperFactory
{
IDatabaseFactory InstallDB();
IOpSystemFactory InstallOpSystem();
}
然后我们定义一个具体的服务器类来实现这个超级工厂,在接口的实现中我们让这个服务器类安装windows system和mysql db。
public class ServerWithWindowsAndMySql : ISuperFactory
{
public IDatabaseFactory InstallDB()
{
return new MySqlFactory();
}
public IOpSystemFactory InstallOpSystem()
{
return new WindowsFactory();
}
}
最后在Main函数中调用看看。
static void Main(string[] args)
{
ISuperFactory server1 = new ServerWithWindowsAndMySql();
server1.InstallDB().CreateDatabase().Delete();
server1.InstallDB().CreateDatabase().Insert();
server1.InstallOpSystem().InstallSystem().Start();
server1.InstallOpSystem().InstallSystem().Shutdown();
}
下面是运行结果。
我们可以看到实现了超级工厂的服务器类同时拥有了创建数据库和安装操作系统的功能。这就是抽象工厂的用法。我们来看看抽象工厂的优缺点。优点:
1.实现了不同工厂之间的解耦。
缺点:
1.代码量成倍的增加
2.抽象工厂并不符合开闭原则。如果这个时候我们需要在超级工厂中添加一个新的工厂,那么具体类也必须要作出修改。
4.总结
工厂模式同样是设计模式中比较常用而且比较容易理解(抽象工厂除外)的设计模式。同时也能加深我们对面向对象中“多态”这个概念的理解:我们只需要关注接口中方法的声明,不用知道具体类有什么方法方法如何实现。换句话说我们只需要声明了一个接口,便可以直接调用接口的方法,当然前提是接口必须由实现该接口的具体类来实例化。同时我们在工作中也必须对设计模式的使用稍加思考,只有我们需要去使用这个设计模式的时候才去使用,如果我们为了使用设计模式而去使用设计模式,我们反而会得到糟糕的效果。