工厂模式与坚实原则

时间:2019-05-13 20:47:31

标签: design-patterns solid-principles

我已经阅读了有关工厂模式的信息,但我想知道它是否与开放式实心原则兼容。在许多示例中,工厂方法使用枚举,然后在switch语句中创建具体对象,请参见wiki page上的C#示例。 听起来很合理,我可能想决定应该基于枚举创建哪个对象。另一方面,当扩展枚举时,我还必须在switch语句中添加一个新的情况,这会破坏“开闭实心原则”。

我还想知道Wiki页面是否真的描述了工厂方法模式或称为简单工厂的东西。我可以创建以下界面:

public interface IPersonFactory
{
    IPerson CreatePerson();
}

然后实施一个具体工厂,例如:

public class VillagerFactory : IPersonFactory
{
    public IPerson CreatePerson()
    {
        return new Villager();
    }
}

然后基于枚举,我可以决定应使用哪种工厂实现,但是再次扩展枚举时,我还必须在代码的某个位置的switch语句中添加一个case,所以我不确定第二种方法是否更好。

您能澄清一下吗?

2 个答案:

答案 0 :(得分:1)

GoF的“工厂方法”模式与枚举无关,并且实际上与“开放-封闭原则”兼容。

假设您正在编写类C。在C内部的某些代码上,您可以使用new关键字来创建某个类X的对象。

稍后,您需要在某个地方重用类C,但需要自定义。唯一需要的自定义方法是在C内要创建X的某个派生类的对象,例如X'

然后,通过使类C更抽象来应用Factory Method模式:添加具有签名的抽象方法,返回类型为X的对象,将对new X的每次调用替换为调用抽象方法。类C的其余代码保持不变。

我们将该抽象方法称为 Factory方法

现在,您可以在某个地方重用类C:编写类C'继承C,实现抽象方法,让其创建(通常使用new关键字),然后返回所需类型为XX'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