c# - 为什么我们已经有了具体工厂,还需要抽象工厂
问题描述
这是文章
https://www.codeproject.com/Articles/328373/Understanding-and-Implementing-Abstract-Factory-Pa
我不知道为什么我们需要有一个抽象工厂IPhoneFactory
,在客户端,我们不能做某事吗
class PhoneTypeChecker
{
ISmart sam;
IDumb htc;
//IPhoneFactory factory; get rid of this abstract factory
MANUFACTURERS manu;
...
public void CheckProducts()
{
switch (manu)
{
case MANUFACTURERS.SAMSUNG:
Console.WriteLine(new SamsungFactory().GetSmart().Name());
Console.WriteLine(new SamsungFactory().GetDumb().Name());
break;
case MANUFACTURERS.HTC:
Console.WriteLine(new HTCFactory().GetSmart().Name());
Console.WriteLine(new HTCFactory().GetDumb().Name());
break;
case MANUFACTURERS.NOKIA:
Console.WriteLine(new NokiaFactory().GetSmart().Name());
Console.WriteLine(new NokiaFactory().GetDumb().Name());
break;
}
Console.WriteLine(manu.ToString() + ":\nSmart Phone: " +
factory.GetSmart().Name() + "\nDumb Phone: " + factory.GetDumb().Name());
}
}
我唯一能想到使用IPhoneFactory
的是我们需要从具体工厂类中获取两种不同的类型(Smart 和 Dumb),但它只会节省一些击键,不是吗?
解决方案
您链接到的文章存在一个核心问题,这意味着它没有给出一个很好的例子来说明如何使用抽象工厂来使应用程序更具可扩展性:
问题是,两者enum Manufacturers
都class PhoneTypeChecker.CheckProducts
必须了解所有类型的制造商。这几乎完全违背了抽象工厂模式的目的(除非您将其解释CheckProducts()
为“抽象工厂”方法——在这种情况下,欢迎来到 Enterprise Java hell。
...但是如果我们对构图进行一些调整,优势就会变得明显。
首先,删除enum MANUFACTURER
(无论如何都不应该大写)并将CheckProducts
其替换为IManufacturerRepository
.
- 重要提示:如果您实际上正在编写与 ORM 接口的真实代码,那么不要创建自己的存储库类型,因为您的 ORM 为您提供了存储库。对于实体框架尤其如此。尤其要避免“通用存储库”反模式。
IManufacturerRepository
应该只有一个实现(或用于测试/模拟的单独实现)。
public interface IManufacturerRepository
{
List<IManufacturer> GetManufacturers();
}
public interface IManufacturer
{
String Name { get; }
IPhoneFactory PhoneFactory { get; } // <-- this is the abstract factory
}
然后PhoneTypeChecker
可以接收IManufacturer
via 构造函数参数:
- 我注意到这个例子很愚蠢,因为内部没有有意义的实例状态突变,
PhoneTypeChecker
也不代表PhoneTypeChecker
由抽象类型或接口表示的任何类型的操作 - 这最好通过制作CheckProducts
一个static
接受IManufacturer
作为参数的方法来完成)。 - 在原始文章中,该
CheckProducts
方法确实改变了PhoneTypeChecker
的状态(通过设置factory
,sam
,htc
),但这是一个糟糕的设计- 因为它实际上只是在初始化自身,这应该在构造函数中完成 - 但我离题了。
反正:
public class PhoneTypeChecker
{
private readonly IManufacturer mfg;
public PhoneTypeChecker( IManufacturer mfg )
{
this.mfg = mfg ?? throw new ArgumentNullExceptio(nameof(mfg));
}
public void CheckProducts()
{
Console.WriteLine( this.mfg.Name + ":\nSmart Phone: " +
this.mfg.PhoneFactory.GetSmart().Name() + "\nDumb Phone: " + this.mfg.PhoneFactory.GetDumb().Name() );
}
}
然后该Main
方法将重做为:
static void Main(string[] args)
{
IManufacturerRepository repo = new ActualIManufacturerRepository();
List<IManufacturer> mfgs = repo.GetManufacturers();
PhoneTypeChecker checker = new PhoneTypeChecker( mfgs.Single( m => m.Name == "Samsung" ) );
checker.CheckProducts();
Console.ReadLine();
checker = new PhoneTypeChecker( mfgs.Single( m => m.Name == "HTC" ) );
checker.CheckProducts();
Console.ReadLine();
checker = new PhoneTypeChecker( mfgs.Single( m => m.Name == "Nokia" ) );
checker.CheckProducts();
Console.Read();
}
这个例子还有其他问题——所以不要拿这个例子来说明如何编写结构良好或生产质量的代码。
简而言之:只有 的实现才IManufacturerRepository
应该知道 的实际实现IManufacturer
(并且只有 的实现才IManufacturer
应该知道 的实现IPhoneFactory
)。
推荐阅读
- python-3.x - 在嵌套字典中查找特定键的值
- python-3.x - 从python中的大文件中读取一些特定的行
- r - 如何生成分组堆积条形图
- xcode11 - 如何在 Xcode 设置中删除空格并返回符号
- android - Android:无法使用 videoview 播放视频
- javascript - html css jquery modal 没有在浏览器中打开
- python - Python:列表索引超出范围/追加列表
- python - 从源代码构建 Python 性能
- header - 在 jupyter notebook 中带有自定义编号的 Markdown 标头
- tkinter - 如何创建比例,使用键盘箭头控制它,并使用 tkinter 记录单个值