工厂方法模式之旅






2.37/5 (9投票s)
用各种方法说明工厂方法模式
灵感
当我第一次接触工厂方法模式时,它看起来很简单。由于没有文章具有确凿的真实性,我浏览了许多网站,发现每个人都在使用不同的实现方式,这让我感到困惑。
我脑海中开始出现很多问题,例如:在哪些场景下应该使用它?有没有固定的代码来实现它?工厂模式和工厂方法模式有什么区别,或者它们是相同的吗?如果对象的实例化从 UI 代码推迟到子类,那么实例化究竟应该发生在工厂内部还是产品内部?
在名为FACTORY
的设计模式名称中一直存在很多混淆,有人说工厂,有人说简单工厂、工厂模式、工厂方法、工厂方法设计模式、抽象工厂。
因此,我决定在一个文章中使用相同的示例进行所有说明,这样就可以轻松地比较不同的概念。
引言
这是“四人帮”1定义的创建型设计模式中的第二种设计模式。
此模式在于将对象实例化推迟到子类。
GoF 没有定义“工厂模式”,但许多网站 (3, 4, 5, 6, 7, 8)确认了它的存在,因为它非常有用且被广泛使用,它是由“Garry Woodfine” (2, 3)定义的。因此,在这里我将尝试描述所有与工厂方法相关的实现方法。
谁应该阅读本文?
- 如果您对设计模式是新手。
- 如果您对工厂方法模式是新手。
- 如果您对工厂模式和工厂方法模式感到困惑。
- 如果您阅读了许多关于工厂方法的示例,但仍然感到困惑,因为您无法将代码与类图进行映射。
- 如果您想了解模式中的每个类做什么以及如何做。
关于本文
本文将从现实世界的场景切换到编程世界,因此故事会在两种语言之间切换。由于我尝试将代码语言与现实生活语言进行映射,您可能需要通过仔细阅读和比较代码与解释来理解。有时,您可能会觉得代码可以缩短,但同样,这只是为了与现实世界的例子进行映射。有时,您会发现两个不同阶段的同一个类没有变化,但这是故意的,以便您能够识别出变化发生在哪里。
代码中提供了足够多的代码注释,描述了代码的用途。希望您能享受这次旅程。
现实世界中的一个例子 - 榨汁机/搅拌机/研磨机
故事从厨房开始,这是每个人都需要的地方!既然是关于为日常厨房的混合产品问题寻找正确的解决方案,那将是一个漫长的过程。如果您想一气呵成,这里不是合适的地方。
目录
第一阶段 - 没有接口的简单类
现实世界场景
几年前,我很少想吃煎蛋卷,所以只用勺子搅拌鸡蛋。到目前为止一切都很好。我需要多想一下吗,可能不需要?
是的……有时候,我也需要混合果泥或果酱,但这些也可以用一个更大的罐子或杯子和勺子来完成。
我本可以买一台搅拌机,但对于这种情况来说,我不确定是否经济。
用编程术语来说
所以,有时候,如果我们(作为主类)直接实例化一个对象而不委托责任,如果它是一次性的需求,那就足够了。
类图
实现代码
UIClass.cs
using System;
namespace SimpleClass
{
/// <summary>
/// This is very first example of class instantiation,
/// no inheritance or interface concept being used here.
/// Observations -
/// UI - Main class calling 2 different class for Egg and Pulp
/// Product - EggProduct, PulpProduct
/// Problem - Whenever a new product is added, separate process need to be added
/// </summary>
class UIClass
{
static void Main(string[] args)
{
Console.WriteLine("Please enter item to blend : Egg or Pulp");
string strUserInput = Console.ReadLine();
switch (Console.ReadLine())
{
case "Egg":
// EggCup egg = new EggCup();
UseCupForEgg egg = new UseCupForEgg();
egg.BlendNMixEgg("EggLiquid");
break;
case "Pulp":
UseJarForPulp puree = new UseJarForPulp();
puree.MixMangoPulp("MilkShake");
break;
}
Console.ReadKey();
}
public class UseCupForEgg
{
public void BlendNMixEgg(string readyProduct)
{
Console.WriteLine(readyProduct + "Egg is mixed");
}
}
public class UseJarForPulp
{
public void MixMangoPulp(string readyProduct)
{
Console.WriteLine(readyProduct + "Mango shake is prepared.");
}
}
}
}
对第一阶段的观察
我观察到的是,我总是对混合相似的东西执行相同的过程,并且最终产品的类型是相同的,那么为什么我不能对这两种过程使用相同的器具?我能否优化和改进事物?毕竟,将要用于获得相同类型的最终产品(液体)的定义过程(混合)是相同的。
第二阶段 - 没有工厂但有定义的接口
现实世界场景
所以,现在,我有一个罐子用于混合过程,将鸡蛋或果泥转化为液体。
用编程术语来说
单一 UI 使用相同过程将多种原材料转化为相同的抽象产品。
类图
实现代码
UIClass.cs
using System;
/// <summary>
/// This is enhanced version of class instantiation by introducing product interface
/// implementation for reusability and interaction of common functionalities.
/// Observations -
/// 1. Object instantiation of product
/// 2. UI - Main class calling 2 different class for Egg and Pulp but calling same methods
/// What's new here -
/// 1. UI - Unified UI(Utencil) calling same method(BlendProduct)
/// Problem -
/// 1. Every time UI code need to change while adding a new item
/// 2. OCP violation
/// </summary>
namespace NoFactory
{
/// <summary>
/// This is UI code instantiating product classes
/// </summary>
class UIClass
{
static void Main(string[] args)
{
Console.WriteLine("Please enter item for Mixer : Egg, Pulp");
IMixedProduct endProduct = null;
switch (Console.ReadLine())
{
case "Egg":
endProduct = new EggProduct();
endProduct.BlendProduct("EggLiquid");
break;
case "Pulp":
endProduct = new PureeProduct();
endProduct.BlendProduct("MilkShake");
break;
}
Console.ReadKey();
}
}
}
Product.cs
/// <summary>
/// Observations -
/// 1. Products - (Two) EggProduct, PulpProduct
/// What's new here -
/// 1. Product - Unified abstract product defines same method to be used by different child products
/// </summary>
namespace NoFactory
{
/// <summary>
/// Its an essential function of blender, no matter what kind of product its going to mix
/// </summary>
public interface IMixedProduct
{
void BlendProduct(string readyProduct);
}
/// <summary>
/// Concrete product egg type inheriting abstract product
/// </summary>
public class EggProduct : IMixedProduct
{
public void BlendProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " is prepared.");
}
}
/// <summary>
/// Concrete product puree type inheriting abstract product
/// </summary>
public class PureeProduct : IMixedProduct
{
public void BlendProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " is prepared.");
}
}
}
第二阶段存在的问题
过了一段时间,我需要为家人更频繁、更大批量地制作煎蛋卷。因此,在数量和频率方面的需求增加了,我花费了更多时间来混合许多鸡蛋。
此外,我还需要制作奶昔或偶尔打成番茄泥。因此,每当我需要研磨其他物品时,要么需要修改同一个器具,要么需要使用不同的器具(即 UI)。
让我们通过代码来理解。
UIClass.cs
using System;
/// <summary>
/// This is enhanced version of class instantiation by introducing product interface
/// implementation for reusability and interaction of common functionalities.
/// Observations -
/// 1. Object instantiation of product
/// 2. UI - Main class calling 2 different class for Egg and Pulp but calling same methods
/// What's new here -
/// 1. UI - Unified UI(Utencil) calling same method(BlendProduct)
/// Problem -
/// 1. Every time UI code need to change while adding a new item
/// 2. OCP violation
/// </summary>
namespace ProblemWithNoFactory
{
/// <summary>
/// This is UI code instantiating product classes
/// </summary>
class UIClass
{
static void Main(string[] args)
{
Console.WriteLine("Please enter item for Mixer : Egg, Pulp");
IMixedProduct endProduct = null;
switch (Console.ReadLine())
{
case "Egg":
endProduct = new EggProduct();
endProduct.BlendProduct("EggLiquid");
break;
case "Pulp":
endProduct = new PureeProduct();
endProduct.BlendProduct("MilkShake");
break;
// New items to be processed
case "Sauce":
endProduct = new SauceProduct();
endProduct.BlendProduct("Sauce");
break;
case "Puree":
endProduct = new PulpProduct();
endProduct.BlendProduct("Puree");
break;
}
Console.ReadKey();
}
}
}
Product.cs
using System;
/// <summary>
/// Observations -
/// 1. Products - (Four) EggProduct, PulpProduct, SauceProduct, PulpProduct
/// What's new here -
/// 1. Product - Unified abstract product defines same method to be used by different child products
/// </summary>
namespace ProblemWithNoFactory
{
/// <summary>
/// Its an essential function of blender, no matter what kind of product its going to mix
/// </summary>
public interface IMixedProduct
{
void BlendProduct(string readyProduct);
}
/// <summary>
/// Concrete product egg type inheriting abstract product
/// </summary>
public class EggProduct : IMixedProduct
{
public void BlendProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " is prepared.");
}
}
/// <summary>
/// Concrete product puree type inheriting abstract product
/// </summary>
public class PureeProduct : IMixedProduct
{
public void BlendProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " is prepared.");
}
}
// New items to be added
public class SauceProduct : IMixedProduct
{
public void BlendProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " is prepared.");
}
}
public class PulpProduct : IMixedProduct
{
public void BlendProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " is prepared.");
}
}
}
此处注意点
违反 OCP(开放封闭原则)- 类应该对扩展开放,对修改关闭,但这里不是。
需求分析 -(需要是发明的母亲)
- 我需要一个设备,可以快速混合各种半液体物品。
- 我不想每次都更换混合器具。
- 我希望得到相同形式的输出。
第三阶段 - 简单工厂(也称为工厂、工厂模式、工厂设计模式)
解决方案(在现实世界场景中)
因此,我考虑购买一台搅拌机,它可以帮助我在更短的时间内混合更多的鸡蛋。我也可以用它来混合任何果泥制作酱料,以及混合芒果泥制作奶昔。所以它只适用于混合半液体物品。
用编程术语来说
在这里,我们将对象创建的责任委托给工厂(搅拌机)。我们将修改示例以遵循 OCP 和 SRP(单一类承担单一类型的责任)。
定义
“它是一个类,其方法负责根据其接收的输入类型创建对象”(在此示例中,即半液体 - 鸡蛋、果泥、芒果泥等)。
简而言之,工厂有助于将所有对象创建集中在一个地方,并避免在新键值对散布在整个代码库中。(3)
它创建对象而不将实例化逻辑暴露给客户端,并通过通用接口引用新创建的对象。 (8)
注意事项
- 它可能有一个工厂接口,也可能没有,但这并非强制。
- 工厂类可能有一个
static
方法,也可能没有,这取决于如何在 UI 中调用工厂。
类图
实现代码
UIClass.cs
using System; /// <summary> /// Observations - /// 1. Object instantiation of product of factory not the products /// What's new here - /// 1. UI - Main class calling a factory not 2 different class for Egg and Pulp /// 2. UI code will not be changed, while using new items, /// but it has to be of same type which is semi-liquid /// 3. Follows - OCP, SRP /// Problem - Limited to handle one type of item only /// </summary> namespace SimpleFactoryPattern { /// <summary> /// UI class /// </summary> class UIClass { static void Main(string[] args) { Console.WriteLine("Please enter item for Mixer : Egg, Pulp, Sauce, Puree"); BlenderFactory liquid = new BlenderFactory(); liquid.GetMixingProduct("SemiLiquid", Console.ReadLine()); Console.ReadKey(); } } }
Factory.cs
/// <summary> /// A Factory with the responsibility of object instantiation /// Observation - /// 1. UI code will not be changed, in case product is changed /// 2. Single class handling instantiation of child product /// 3. Object instantiation of product /// </summary> namespace SimpleFactoryPattern { /// <summary> /// Single factory handling instantiation of all products of same type /// </summary> public class BlenderFactory { public string GetMixingProduct(string inputType, string itemName) { string readyProduct = null; if (inputType == "SemiLiquid") { switch (itemName) { case "Egg": IMixedProduct egg = new MixtureProduct(); egg.BlendProduct(itemName); break; case "Pulp": IMixedProduct pulp = new MixtureProduct(); pulp.BlendProduct(itemName); break; case "Sauce": IMixedProduct sauce = new MixtureProduct(); sauce.BlendProduct(itemName); break; case "Puree": IMixedProduct puree = new MixtureProduct(); puree.BlendProduct(itemName); break; } } return readyProduct; } } }
Product.cs
/// Observations - /// 1. Products - (One) MixtureProduct /// What's new here - /// 1. Product - Unified product implements method defined in interface /// </summary> namespace SimpleFactoryPattern { /// <summary> /// Its an essential function of blender, no matter what kind of product its going to mix /// </summary> public interface IMixedProduct { void BlendProduct(string readyProduct); } /// <summary> /// End product type implementing method defined in interface /// </summary> public class MixtureProduct : IMixedProduct { public void BlendProduct(string readyProduct) { Console.WriteLine(readyProduct + " is mixed."); } } }
第三阶段存在的问题
到目前为止一切都很好,但有一天我赶时间,需要捣碎煮熟的番茄来做咖喱。
需求分析 -(欲望永无止境)
我绞尽脑汁——我可以捣碎煮熟的土豆,那么我应该买一个搅拌机吗,也许现在还为时过早,让我先考虑一个更便宜、更经济的选择。我比较了搅拌机和榨汁机的相似属性,它们都有刀片和电机,并且都有混合物品的功能,并且都能产生相似的液体输出。
第四阶段 - 具有多种职责的单一工厂(不推荐)
解决方案(在现实世界场景中)
所以我用手捣碎了番茄,然后用搅拌机混合,结果奏效了,成品没有达到所需的精细度,但满足了基本需求,即捣碎的番茄。
用编程术语来说
在这里,我们调用了额外的 BOILED(煮熟的)物品的处理方法,以便在使用abstract
方法之前对其进行准备。
类图
实现代码
UIClass.cs
using System;
namespace FactoryWithMultipleResponsibility
{
/// <summary>
/// Observations -
/// 1. Object instantiation of factory not the products
/// What's new here -
/// 1. UI - Main class calling factory not 2 classes for liquid and boiled item type
/// Problem -
/// 1. UI class with multiple type of responsibility
/// 2. UI code will be changed, if additional responsibility comes in scope
/// </summary>
class UIClass
{
static void Main(string[] args)
{
Console.WriteLine("Please enter item for Mixer : Liquid, or Boiled for mixing.");
string itemName = Console.ReadLine();
switch (itemName)
{
case "Liquid":
BlenderFactory liquid = new BlenderFactory();
liquid.GetMixingProduct("SemiLiquid", itemName);
//BlenderFactory.GetMixingProduct("SemiLiquid", itemName);
break;
case "Boiled":
BlenderFactory mash = new BlenderFactory();
mash.GetMixingProduct("Boiled", itemName);
//BlenderFactory.GetMixingProduct("SemiLiquid", itemName);
break;
}
Console.ReadKey();
}
}
}
Factory.cs
/// <summary>
/// A Factory with the responsibility of object instantiation
/// Observation -
/// 1. Factory need to inherit MashedProduct for one of its two input types
/// Problem -
/// 1. Breaking SRP - As per concept its breaking SRP - So here you can see,
/// there are more than 1 reason to change the class
/// Whenever a new type like solid type is added, factory code need to be changed.
/// </summary>
namespace FactoryWithMultipleResponsibility
{
/// <summary>
/// Single factory with multiple responsibility
/// </summary>
public class BlenderFactory : MashedProduct
{
public string GetMixingProduct(string inputType, string itemName)
{
string readyProduct = null;
if (inputType == "SemiLiquid")
{
switch (inputType)
{
case "Egg":
IMixedProduct egg = new MixtureProduct();
egg.BlendProduct(itemName);
break;
case "Pulp":
IMixedProduct puree = new MixtureProduct();
puree.BlendProduct(itemName);
break;
}
}
if (inputType == "Boiled")
{
switch (inputType)
{
case "BoiledTomato":
// Additional responsibility for mashing boiled items
MashedProduct mashTomato = new MashedProduct();
mashTomato.MashProduct(itemName);
IMixedProduct boildedPotato = new MixtureProduct();
boildedPotato.BlendProduct(itemName);
break;
case "BoiledPotato":
// Additional responsibility for mashing boiled items
MashedProduct mashPotato = new MashedProduct();
mashPotato.MashProduct(itemName);
IMixedProduct boildedTomato = new MixtureProduct();
boildedTomato.BlendProduct(itemName);
break;
}
}
return readyProduct;
}
}
}
Product.cs
/// <summary>
/// Observations -
/// 1. Products - (Two) MixtureProduct
/// What's new here -
/// 1. Product - One additional type of product is added
/// </summary>
namespace FactoryWithMultipleResponsibility
{
/// <summary>
/// Its an essential function of blender, no matter what kind of product its going to mix
/// </summary>
public interface IMixedProduct
{
void BlendProduct(string readyProduct);
}
/// <summary>
/// Concrete product implementing essential method
/// </summary>
public class MixtureProduct : IMixedProduct
{
public void BlendProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " is mixed.");
}
}
/// <summary>
/// A class for additional function of multiple responsibility
/// </summary>
public class MashedProduct
{
public void MashProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " is mixed.");
}
}
}
第四阶段存在的问题
首先,它的输出不尽如人意,因为我是用手捣碎土豆的;其次,它无法满足制作果汁或研磨蔬菜等需求。
用编程术语来说
有时,我们试图将同一个类用于它并非为之设计的职责,仅仅因为它有一些相似的方法和属性,并且我们想节省时间和精力。
注意点
违反 SOLID SRP 规则 - 将多种类型的职责分配给一个类。一个类应该只有一个改变的原因。
需求分析
我兴致勃勃地探索厨房里通常还需要什么,以及我能用这个搅拌机做什么?因此,通常,我还需要研磨蔬菜来制作冰沙或研磨谷物来制作面团,所以为什么不拥有另一个设备呢?
第五阶段 - 使用多个工厂 - 可行性较低的解决方案
解决方案(在现实世界场景中)
我决定为所有其他物品,如固体或半固体物品,购买一个单独的搅拌机,这样我就可以获得预期的其他物品的输出。
用编程术语来说
我决定将不同的职责委托给每个产品类型的独立工厂,以避免违反 SRP。
类图
实现代码
UIClass.cs
using System;
/// <summary>
/// Observations -
/// 1. Object instantiation of specific factories depends on input type
/// What's new here -
/// 1. UI - Main class calling specific concrete factories according to input type
/// Problem -
/// 1. UI class with multiple type of responsibility
/// 2. UI code will be changed, if additional responsibility comes in scope
/// 3. Maintaining consistency among factories becomes complicated here
/// </summary>
namespace MultipleFactories
{
/// <summary>
/// UI code calling specific factories as per input type
/// </summary>
class UIClass
{
static void Main(string[] args)
{
// Code added for mixing and grinding items
Console.WriteLine("Please enter item for Mixer : Fruit, Boiled, or Grain");
string itemType = Console.ReadLine();
switch (itemType)
{
case "Fruit":
JuiceFactory juice = new JuiceFactory();
juice.GetJuiceProduct(itemType);
break;
case "Boiled":
MashingFactory semiLiquid = new MashingFactory();
semiLiquid.GetMashProduct(itemType);
break;
case "Grain":
GrindingFactory flour = new GrindingFactory();
flour.GetGrindedProduct(itemType);
break;
}
Console.ReadKey();
}
}
}
Factory.cs
/// <summary>
/// What's New -
/// 1. There are separate factory for all type like juice, boiled, flour
/// Problem -
/// 1. There is repetition of code involve in each factory,
/// and if it require changes, changes need everywhere
/// Object instantiation of product
/// </summary>
namespace MultipleFactories
{
/// <summary>
/// Concrete juice factory calling required methods
/// </summary>
public class JuiceFactory
{
public JuiceProduct GetJuiceProduct(string inputType)
{
JuiceProduct juice = new JuiceProduct();
string readyProduct = juice.sqeezeProduct();
IMixedProduct endProduct = new MixtureProduct();
endProduct.MixProduct(readyProduct);
return juice;
}
}
/// <summary>
/// Concrete mashing factory calling required methods
/// </summary>
public class MashingFactory
{
public MashedProduct GetMashProduct(string inputType)
{
MashedProduct mash = new MashedProduct();
string readyProduct = mash.mashProduct();
new MixtureProduct().MixProduct(readyProduct);
return mash;
}
}
/// <summary>
/// Concrete grinding factory calling required methods
/// </summary>
public class GrindingFactory
{
public FlourProduct GetGrindedProduct(string inputType)
{
FlourProduct flour = new FlourProduct();
string readyProduct = flour.grindProduct();
new MixtureProduct().MixProduct(readyProduct);
return flour;
}
}
}
Product.cs
using System;
/// <summary>
/// Observations -
/// 1. Products - One child product inheriting abstract product and three separate products
/// What's new here -
/// 1. Product - One additional type of product is added
/// </summary>
namespace MultipleFactories
{
/// <summary>
/// Its an essential function of blender, no matter what kind of product its going to mix
/// </summary>
public interface IMixedProduct
{
void MixProduct(string readyProduct);
}
/// <summary>
/// Concrete product implementing essential method
/// </summary>
public class MixtureProduct : IMixedProduct
{
public void MixProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " is mixed.");
}
}
/// <summary>
/// Squeezing function for juices only
/// </summary>
public class JuiceProduct
{
public string sqeezeProduct()
{
return "Juice";
}
}
/// <summary>
/// Mashing function for boiled item only
/// </summary>
public class MashedProduct
{
public string mashProduct()
{
return "Mashed product";
}
}
/// <summary>
/// Grinding function for grain items only
/// </summary>
public class FlourProduct
{
public string grindProduct()
{
return "Flour";
}
}
}
第五阶段存在的问题
我以为我已经完成了厨房电器的配置,但事情并未就此结束,过了一段时间,我需要研磨小麦来制作面团。我既不能使用搅拌机或研磨机来研磨小麦,也不想在我的厨房里购买(即额外的努力)另一个电器(即管理代码)。
用编程术语来说 / 注意点 / 需求分析
- 我想获得所有液体、半液体、固体物品(如水果、蔬菜、小麦、果泥、番茄、土豆等)的所有研磨功能。
- 我不想牺牲特定的结果或质量。
- 我不想购买很多电器,因为这既不经济,也不易于管理。
第六阶段 - 工厂方法模式
(也称为工厂方法、工厂方法模式、工厂方法设计模式)
解决方案
因此,我没有购买许多电器,而是购买了一个带有独立罐子的组合式JuicerMixerGrinder
(榨汁机/搅拌机/研磨机)。
现实世界场景
当您需要水果汁时,您会寻找榨汁机,这就像一个小工厂(搅拌机 - 创建者,罐子 - 具体创建者,鸡蛋/水果 - 原材料,果泥/果汁 - 具体产品)。因此,根据您放入搅拌机中的物品(输入),它可以产生果泥、奶昔或果汁。因此,当搅拌机搅拌或研磨物品时,它不知道它将要压碎哪个物品(即,让子类决定要实例化哪个类)。
定义(用编程术语来说)
“工厂方法模式定义了一个用于创建对象的接口,但让子类决定实例化哪个类。工厂方法允许类将实例化推迟到子类。”
将设计模式术语与现实世界示例进行比较
- 您 - 主方法
- 搅拌机 - 工厂或创建者
- 罐子 - 具体创建者
- 物品 - 原材料
解决方案(用编程术语来说)
这里有两种方法
方法 1
可能存在多个具体工厂,它们实现特定的方法,具体取决于输入类型和工厂接口中定义的必要(所有产品共有的方法,即混合)方法来实例化最终产品。
方法 2
可能存在一个具体的工厂,它根据输入类型实现产品特定的方法,然后工厂接口中定义了必要(所有产品共有的方法,即混合)方法来实例化最终产品。
第六阶段(方法一)
类图
实现代码
UIClass.cs
using System;
/// <summary>
/// Observations -
/// 1. Object instantiation of specific factories depends on input type
/// 2. Caller of factory(UI Class in this example) knows which factory to call,
/// but don't know which end product to prepare
/// What's new here -
/// 1. This is one of the approaches for Factory method pattern,
/// where UI class initialize specific concreate factory
/// </summary>
using System;
namespace FactoryMethod_App1
{
/// <summary>
/// UI code calling specific factories as per input type
/// </summary>
class UIClass
{
static void Main(string[] args)
{
// Code added for mixing and grinding items
Console.WriteLine("Please enter item for Mixer : Fruit, Boiled, or Grain");
string itemType = Console.ReadLine();
switch (itemType)
{
case "Fruit":
// Instantiating concreate factory
IMixerFactory juice = new JuiceFactory();
//Creation of product using method in factory (that is factory method)
IMixer mixJuice = juice.GetMixingProduct();
// Processing end product
mixJuice.MixProduct(itemType);
// above 2 lines of code can also be written in single line
//juice.GetMixingProduct().MixProduct(itemType);
break;
case "Boiled":
IMixerFactory boiled = new MashingFactory();
IMixer mash = boiled.GetMixingProduct();
mash.MixProduct(itemType);
break;
case "Grain":
IMixerFactory crush = new GrindingFactory();
IMixer flour = crush.GetMixingProduct();
flour.MixProduct(itemType);
break;
default :
throw new Exception("item type not supported");
}
Console.ReadKey();
}
}
}
Factory.cs
/// <summary>
/// Observations -
/// 1. There are seperate concreate factories for all type like juice, boiled, flour
/// 2. Here mixer has 3 separate jars for juicer, masher, & grinder
/// What's New -
/// 1. Introduceds single factory interface to be implemented by all concreate factories
/// </summary>
namespace FactoryMethod_App1
{
/// <summary>
/// Observations -
/// Single factory interface to be implemented by all products
/// </summary>
public interface IMixerFactory
{
IMixer GetMixingProduct();
}
/// <summary>
/// Concreate juice factory calling required methods
/// </summary>
public class JuiceFactory : IMixerFactory
{
public IMixer GetMixingProduct()
{
return new Juicer();
}
}
/// <summary>
/// Concrete mashing factory calling required methods
/// </summary>
public class MashingFactory : IMixerFactory
{
public IMixer GetMixingProduct()
{
return new Masher();
}
}
/// <summary>
/// Concrete grinding factory calling required methods
/// </summary>
public class GrindingFactory : IMixerFactory
{
public IMixer GetMixingProduct()
{
return new Grinder();
}
}
}
Product.cs
using System;
/// <summary>
/// Observations -
/// 1. Each function is implementing interface method to process end product
/// </summary>
namespace FactoryMethod_App1
{
/// <summary>
/// Product interface
/// </summary>
public interface IMixer
{
void MixProduct(string readyProduct);
}
/// <summary>
/// Juicer implements interface method
/// </summary>
public class Juicer : IMixer
{
public void MixProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " juice is prepared.");
}
}
/// <summary>
/// Masher implements interface method
/// </summary>
public class Masher : IMixer
{
public void MixProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " product is mashed.");
}
}
/// <summary>
/// Grinder implements interface method
/// </summary>
public class Grinder : IMixer
{
public void MixProduct(string readyProduct)
{
Console.WriteLine(readyProduct + " flour is prepared.");
}
}
}
第六阶段(方法二)
类图
实现代码
UIClass.cs
using System;
/// <summary>
/// Observations -
/// 1. This is another approach for Factory method pattern,
/// where UI class initialize single concreate factory
/// 2. Object instantiation of main concreate factories depends on input type using interface
/// </summary>
using System;
namespace FactoryMethod_App2
{
/// <summary>
/// UI code calling specific factories as per input type
/// </summary>
class UIClass
{
static void Main(string[] args)
{
// Code added for mixing and grinding items
Console.WriteLine("Please enter item for Mixer : Fruit, Boiled, or Grain");
// Note - Using this method, UI code will not be changed.
IMixerFactory liquid = new MixerFactory();
IMixer endproduct = liquid.GetMixingProduct(Console.ReadLine());
Console.ReadKey();
}
}
}
Factory.cs
/// <summary>
/// Observations -
/// 1. There is only one factory for all type like juice, boiled, flour
/// 2. Here mixer has 3 function for juicer, masher, & grinder
/// What's New -
/// 1. Single factory interface to be implemented by single concreate factory
/// </summary>
namespace FactoryMethod_App2
{
/// <summary>
/// Observations -
/// Single factory interface to be implemented by all products
/// Abstract class can also be used here instead of interface
/// </summary>
public interface IMixerFactory
{
IMixer GetMixingProduct(string inputType);
}
/// <summary>
/// Single concreate factory instantiating concreate products
/// </summary>
public class MixerFactory : IMixerFactory
{
public IMixer GetMixingProduct(string inputType)
{
return new Mixer(inputType);
}
}
}
Product.cs
using System;
/// <summary>
/// Observations -
/// 1. Here Mixer implements interface method after making product ready to mix
/// 2. Mixer using switch case to get specific product ready before mixing
/// </summary>
namespace FactoryMethod_App2
{
/// <summary>
/// Product interface
/// </summary>
public interface IMixer
{
string GetProductReady(string readyProduct);
}
/// <summary>
/// Common mixing function for all item types
/// </summary>
public class Mixer : IMixer
{
public Mixer(string inputType)
{
string endproduct = GetProductReady(inputType);
Console.WriteLine(inputType + " " + endproduct + " is prepared.");
}
// Getting product ready before mixing
public string GetProductReady(string inputType)
{
string readyProduct = null;
switch (inputType)
{
case "Fruit":
readyProduct = new Juicer().sqeezeProduct();
break;
case "Boiled":
readyProduct = new Masher().mashProduct();
break;
case "Grain":
readyProduct = new Grinder().grindProduct();
break;
default :
throw new Exception("item type not supported");
}
return readyProduct;
}
}
/// <summary>
/// Squeezing function for fruit item
/// </summary>
public class Juicer
{
public string sqeezeProduct()
{
return "juice";
}
}
/// <summary>
/// Mashing function for boiled item only
/// </summary>
public class Masher
{
public string mashProduct()
{
return "mashed product";
}
}
/// <summary>
/// Grinding function for solid item only
/// </summary>
public class Grinder
{
public string grindProduct()
{
return "flour";
}
}
}
常见问题解答
- 工厂模式是否总是需要使用 switch 语句?
不,这并非必需,这取决于如何管理具体工厂的实例化。
- 根据定义,工厂方法可以仅在接口中定义吗,它定义了一个接口?
不,也可以使用
abstract
方法。 - 由于此示例使用
Abstract
关键字来定义产品,所以它是否类似于abstract
工厂模式?不,使用
Abstract
这个词是为了唯一地定义一个具有必要方法的产品,abstract
工厂模式具有不同层级的结构。 - 在此示例中,具体工厂方法使用方法组合,那么它是否类似于
abstract
工厂方法?这里使用了组合,只是为了从 UI 中实例化单个方法,在抽象工厂中,
Abstract
工厂的定义内部使用了abstract
关键字与abstract
方法的组合。更多详情,请参阅另一篇关于abstract
工厂的文章。
参考文献
- https://www.dofactory.com/net/factory-method-design-pattern
Factory
- https://garywoodfine.com/
- https://dev.to/gary_woodfine/simple-factory-pattern-in-c-and-net-core-3263
- https://www.oodesign.com/factory-pattern.html
- https://www.c-sharpcorner.com/UploadFile/akkiraju/factory-design-pattern-vs-factory-method-design-pattern/
- http://shop.oreilly.com/product/9780596007126.do
- https://www.c-sharpcorner.com/UploadFile/akkiraju/factory-design-pattern-vs-factory-method-design-pattern/
- https://vivekcek.wordpress.com/2013/03/17/simple-factory-vs-factory-method-vs-abstract-factory-by-example/
关注点
互联网上有很多插图,请仔细阅读几个网站,以了解实际使用它的地方。