我已经阅读了有关工厂模式的信息,但我想知道它是否与开放式实心原则兼容。在许多示例中,工厂方法使用枚举,然后在switch语句中创建具体对象,请参见wiki page上的C#示例。 听起来很合理,我可能想决定应该基于枚举创建哪个对象。另一方面,当扩展枚举时,我还必须在switch语句中添加一个新的情况,这会破坏“开闭实心原则”。
我还想知道Wiki页面是否真的描述了工厂方法模式或称为简单工厂的东西。我可以创建以下界面:
public interface IPersonFactory
{
IPerson CreatePerson();
}
然后实施一个具体工厂,例如:
public class VillagerFactory : IPersonFactory
{
public IPerson CreatePerson()
{
return new Villager();
}
}
然后基于枚举,我可以决定应使用哪种工厂实现,但是再次扩展枚举时,我还必须在代码的某个位置的switch语句中添加一个case,所以我不确定第二种方法是否更好。
您能澄清一下吗?
答案 0 :(得分:1)
GoF的“工厂方法”模式与枚举无关,并且实际上与“开放-封闭原则”兼容。
假设您正在编写类C
。在C
内部的某些代码上,您可以使用new
关键字来创建某个类X
的对象。
稍后,您需要在某个地方重用类C
,但需要自定义。唯一需要的自定义方法是在C
内要创建X
的某个派生类的对象,例如X'
。
然后,通过使类C
更抽象来应用Factory Method模式:添加具有签名的抽象方法,返回类型为X
的对象,将对new X
的每次调用替换为调用抽象方法。类C
的其余代码保持不变。
我们将该抽象方法称为 Factory方法。
现在,您可以在某个地方重用类C
:编写类C'
继承C
,实现抽象方法,让其创建(通常使用new
关键字),然后返回所需类型为X
,X'
,X''
的对象。
如您所见,类C
是开放的:它为抽象方法留出了扩展空间。它已关闭:无需修改其源代码。
通过添加更多的类而不是(或很少通过)修改现有的类来开发软件。这就是OOP的愿景。
答案 1 :(得分:0)
工厂模式有很多用途。我将通过非常简单的示例简要介绍它们及其用途。
注意:这些示例仅用于说明目的,因此其中一些示例似乎有些矫kill过正,而其他示例似乎并不是解决该问题的好方法。为所有事情提供现实的例子很困难,因此我在这里通过忽略一些出于说明目的的原则来给自己一些自由
问题:您有一个包含许多带有不同参数的构造函数的类。这使得代码难以阅读,编写和推理。
解决方案:创建工厂,以简化对象的创建过程,并通过使用多个命名方法来使代码更简洁。
示例:您的系统具有许多不同类型的产品。
public enum ProductType { Book, Paper, Pen }
public class Product {
public ProductType Type { get; set;}
}
public class ProductFactory {
public Product CreateBook() { return new Product(ProductType.Book); }
public Product CreatePaper() { return new Product(ProductType.Paper); }
public Product CreatePen() { return new Product(ProductType.Pen); }
}
问题:您有一个由其他输入和/或通过处理此输入创建的对象。或者您有一个具有复杂创建过程的对象。
解决方案:创建一个工厂,使用命名方法为您完成此任务。
示例:您有一个处理文件系统中文件的系统,并将其扩展名用于复杂操作。您想使用FileExtension class
来处理相等性(在Windows中是不区分大小写的)和其他操作,这样就可以避免使用浮点字符串,避免在不区分大小写的比较中产生错误,并最大程度地使用强类型输入
public class FileExtension { }
public class FileExtensionFactory {
public FileExtension Create(string extension) {
return new FileExtension(extension);
}
public FileExtension FromPath(string fullPath) {
return new FilePath(Path.GetExtension(fullPath);
}
}
public class SomeClassThatUsesTheFactory {
public void DoSomethingWithFileExtension(string filePath) {
var extension = FileExtensionFactory.FromPath(filePath);
// do something with the extension
}
}
在这里您可以看到FromPath
方法的使用使代码更整洁且更具可读性。它还隐藏了filePath
参数的解析方式,并避免了每次您需要从filePath
创建扩展名时都避免重复解析代码。
问题:您需要创建一系列插入到您系统中的相关对象的实例。
解决方案:使用抽象的工厂设计模式。
示例:您正在构建ORM,并且希望支持其他类型的数据库。您想为您的ORM代码将使用的数据库连接定义一个接口。稍后,您希望能够使用不同类型的连接来设置ORM,以便可以连接到不同的数据库。您希望能够将对更多数据库的支持添加到您的ORM。
public interface IDbCommand : IDiposable {
string CommandText { get; set; }
void Execute();
}
public interface IDbConnection : IDisposable {
void Open();
void Close();
IDbCommand CreateCommand();
IDbTransaction BeginTransaction();
// other stuff
}
public interface IDbConnectionFactory {
IDbConnection CreateConnection();
}
public class MySqlCommand : IDbCommand { }
public class MySqlConnection : IDbConnection { }
public class MySqlConnectionFactory : IDbConnectionFactory { }
public class PostgreCommand : IDbCommand { }
public class PostgreConnection : IDbConnection { }
public class PostgreSqlConnectionFactory : IDbConnectionFactory { }
public class OrmSession {
public OrmSession(IDbConnectionFactory) { }
}
在这里,我们看到Open-Closed principle在起作用。您的ORM将使用抽象工厂,因此您可以提供一个具体工厂。您可以从代码中执行此操作,使用配置文件或使用ServiceLocator or DI注入具体工厂。
注意这里还有一些有趣的地方。 IDbConnection
还提供了CreateCommand
FactoryMethod。每个连接都需要创建一个具体的命令,因此即使它不是一个纯粹的工厂,它也提供了一种方法。
在其他情况下,您可以使用工厂。测试其中之一。您可能需要提供一个Abstract Factory,以便可以对应用程序的各个部分进行测试。
结论是,Factory模式具有许多用途。从使代码更整洁和更具表现力到实现“开放式-封闭式”原则,它们可以有所不同。这里的驱动力是您遇到的问题。您在创建对象时遇到问题。工厂可以解决您的问题吗?
并非总是如此。有时,对象的创建是如此复杂,并且具有许多不同的变化,以至于Factory无法提供帮助并且会使事情变得混乱。有时候,您只需要一两个构造函数即可。
在创建复杂异议的情况下,您可以在有或没有BuilderPattern的情况下使用FluentInterface。