C#设计模式之5:简单工厂和工厂方法模式
工厂模式包含三种,简单工厂模式,工厂方法模式,抽象工厂模式。这三种都是解决了一个问题,那就是对象的创建问题。他们的职责就是将对象的创建和对象的使用分离开来。
当我们创建对象的时候,总是会new一个对象,有错么?技术上,new没有错,毕竟是C#的基础部分,真正的犯人是我们的老朋友“改变”。以及他是如何影响new使用的。
针对接口编程,可以隔离掉以后系统可能发生的一大堆改变,为什么呢?如果代码是针对接口而写,那么通过多态,他可以与任何新类实现该接口,但是,当代码使用大量的具体类时,一旦加入新的具体类,就必须改变代码。违反了开闭原则了。
简单工厂
我们先来看一下要订购一个披萨的方法:
public static Pizza OrderPizza(string pizzaType)
{
Pizza pizza;
switch (pizzaType)
{
case "cheese":
pizza= new CheesePizza();
break;
case "ApplePie":
pizza= new ApplePiePizza();
break;
default:
pizza=new SomeOtherPizza();
break;
}
pizza.Prepare();
pizza.Beak();
pizza.Cut();
pizza.Box();
return pizza;
}
如果让我来实现一个订购披萨的系统,我肯定会这么写代码,因为这种的代码是能下意识的就写出来,几乎不用耗费我们的脑筋。但是,仔细看一下这段代码:这个方法的目的是要订购披萨,但是,订购披萨的代码中还要有关于生产pizza的知识,这个方法必须要知道所有的pizza,更加糟糕的是,如果将来要扩充pizza的种类或者删减销量不好的pizza,那么就会来修改这个订购披萨的代码了。看起来很糟糕,应该修改一下。我们第一步要做的一定是要将生产pizza的代码和订购披萨的代码分开来,因为你不应该让订购pizza的代码知道如何生产披萨的逻辑,应该将生产pizza的逻辑单独交给一个专门的类型来负责。这样,就可以应对将来的变化——如果要新增或者删除pizza,我们可以直接修改pizza的生产者而不影响到订购pizza这个逻辑。
而专门生产pizza的这个对象,我们可以叫他为工厂。看一下实现的代码:
public class SimpleFactory
{
public Pizza CreatePizza(string pizzaType)
{
switch (pizzaType)
{
case "cheese":
return new CheesePizza();
case "ApplePie":
return new ApplePiePizza();
default:
return new SomeOtherPizza();
}
}
}
public class PizzaStore
{
private SimpleFactory _factory; public PizzaStore(SimpleFactory factory)
{
this._factory = factory;
}
public Pizza OrderPizza(string pizzaType)
{
var pizza = _factory.CreatePizza(pizzaType);
pizza.Prepare();
pizza.Beak();
pizza.Cut();
pizza.Box();
return pizza;
}
}
把创建对象的逻辑放到一个单独的类中的原因是它可以被多个客户端使用。并且把对象的创建和使用分离开来。但是这样的设计也存在很多缺陷,首先,SimpleFactory是一个具体的类,那么我们就必须针对实现编程,使得系统失去了这方面的弹性,二是如果增加新的产品我们还得修改simplefactory中的代码,这违反了开闭原则。上面这个模式也叫做简单工厂。他没有在GOF的模式中出现,它更像是一个编程的习惯。
可以看到PizzaStore依赖一个SImpleFactory,SimpleFactory依赖一个抽象的pizza。正常情况下,要做解耦,就要做控制反转,控制反转的核心思想是上层结构和底层结构都不依赖实现,而是依赖抽象。
工厂方法模式
现在来做一些改良,直接上代码:
public abstract class FactoryPizzaStore
{
public Pizza OrderPizza(string pizzaType)
{
Pizza pizza = CreatePizza(pizzaType);
pizza .Prepare();
pizza.Beak();
pizza.Cut();
pizza.Box();
return pizza;
}
public abstract Pizza CreatePizza(string pizzaType);
}
现在,PizzaStrore(FactoryPizzaStore)是抽象的。CreatePizza方法从简单工厂移植回来了。在PizzaStroe里面,CreatePizza方法现在是抽象的。
现在已经有一个抽象的PizzaStore类,其他类可以从这个类进行扩展,并重写自己的CreatePizza方法。
解释一下,在OrderPizza方法中,我们做了很多事情,包括调用CreatePizza方法产生一个想要的pizza,然后对pizza进行一系列的操作,关键在于,Pizza被定义为一个抽象类,也就是说在OrderPizza方法中我们并不关心这个Pizza在运行时到底是一个什么类型,换句话说,这就是解耦。解除了PizzaStore和Pizza之间的耦合。
那么现在,原本是通过一个对象负责所有具体类的实例化,现在通过对PizzaStore做的一些改变,变成由一群子类来对具体类的实例化:FactoryPizzaStore的子类负责实现CreatePizza;
更进一步的:工厂方法用来处理对象的创建,并把这种创建对象的实现延迟到子类中去实现,这样,客户代码中关于基类的代码就和子类对象创建代码解耦了。
由于Pizza还是一个抽象类,当我们创建一个FactoryPizzaStore的子类的时候,也应该实现一个具体的继承自Pizza的子类,如果只创建一个FactoryPizzaStore的子类,而没有相对应的Pizza的子类,那么我们的披萨店卖什么呢?
public abstract class Pizza
{
public string Name { get; set; }
public string Dough { get; set; }
public string Sauce { get; set; }
public List<string> Toopings { get; set; } public void Prepare()
{
Console.WriteLine($"Preparing {Name}");
Console.WriteLine("Tossing dough");
Console.WriteLine("Adding sauce");
Console.WriteLine("Adding Toppings..");
foreach (string item in Toopings)
{
Console.WriteLine($" {item}");
}
} public void Cut()
{
Console.WriteLine("Cutting the pizza..");
} public void Bake()
{
Console.WriteLine("Baking the pizza");
} public void Box()
{
Console.WriteLine("Boxing the pizza");
}
}
在创建一些Pizza的子类后,就可以开始测试了:
static void Main(string[] args)
{
FactoryPizzaStore store=new NyPizzaStore();
var pizza = store.OrderPizza("Cheese");
Console.WriteLine(pizza.Name);
Console.ReadKey();
}
认识工厂方法模式:所有的工厂方法模式都是用来创建对象的。工厂方法模式通过让子类决定该创建的对象是什么,来达到将对象创建过程进行封装的目的。工厂方法模式有如下几个角色:
1、创建者:就是上面定义的FactoryPizzaStore。它是一个抽象类,它定义了创建对象的方法,这个方法是一个抽象方法,具体的创建对象的过程由其子类来实现。这个抽象类通常会依赖一个抽象的产品(Product,工厂方法模式中的另一个角色)类。而这些抽象产品由子类制造,创建者并不关心制造的是哪种产品。
2、产品类:就是上面定义的Pizza,它也是一个抽象类,具体的产品由其子类来实现。
我们已经看到,将一个OrderPizza方法和一个工厂方法联合起来(就是FactoryPizzaStore里面的那两个方法),就形成了一个框架。除此之外,工厂方法吧生产知识封装到各个创建者子类,这也可以形成一个框架。
下面给出工厂方法的定义:定义了一个创建对象的接口,但由子类决定要具体创建哪一个。工厂方法让这种创建对象的过程推迟到子类。
简单工厂和工厂方法之间的差异:简单工厂和工厂方法模式看起来很相似,尤其是简单工厂和工厂方法中的具体工厂。简单工厂吧全部的事情,在一个地方都做完了。然而工厂方法创建的是一个框架,让子类决定如何实现。比方说,在工厂方法中,OrderPizza方法创建了一般的框架,以便创建披萨,OrderPizza方法依赖工厂方法创建具体的类,并制造出实际的披萨。可通过继承工厂,决定制造的实际产品到底是什么。简单工厂不具备这方面的弹性。
封装变化
将创建对象的代码封装起来,实例化具体的工厂类,达到了封装实例化目的。将创建对象的代码封装到一个地方,也可以达到复用的目的,并且更方便以后的维护。这也意味着客户在实例化对象时,只会依赖接口,不会依赖具体的实现。帮助我们更好的达到针对接口编程而不是实现,让代码更有弹性,以应对未来的扩展。
C#设计模式之5:简单工厂和工厂方法模式的更多相关文章
- NET设计模式 第二部分 行为型模式(15):模版方法模式(Template Method)
摘要:Template Method模式是比较简单的设计模式之一,但它却是代码复用的一项基本的技术,在类库中尤其重要. 主要内容 1.概述 2.Template Method解说 3..NET中的Te ...
- Java设计模式2:简单工厂模式
简单工厂模式 简单工厂模式是类的创建模式,又叫做静态工厂方法模式.简单工厂模式由一个工厂对象决定生产出哪一种产品类的实例. 为什么要使用简单工厂模式 原因很简单:解耦. A对象如果要调用B对象,最简单 ...
- 大话设计模式C++版——简单工厂模式
简单工厂模式应该是所有设计模式中最简单,也最基础的一种模式,以下是一个简单的采用工厂模式写一个加减法的计算器. 1.抽象接口类——依赖倒转原则(高层和底层都要依赖于抽象,针对接口编程) class I ...
- IOS设计模式浅析之简单工厂模式(SimpleFactory)
概述 首先说明一下,简单工厂模式不属于23种GOF设计模式之一.它也称作静态工厂方法模式,是工厂方法模式的特殊实现.这里对简单工厂模式进行介绍,是为本系列后面的工厂方法和抽象工厂模式做一个引子. 定义 ...
- 设计模式C#实现(九)——工厂方法模式和简单工厂
工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类.Factory Method使一个类的实例化延迟到其子类. 构成: 1.Product工厂方法创建的对象的接口 2.Concrete ...
- Java设计模式之工厂模式(简单工厂模式+工厂方法模式)
摘自http://blog.csdn.net/jason0539/article/details/23020989 在面向对象编程中, 最通常的方法是一个new操作符产生一个对象实例,new操作符就是 ...
- Java设计模式之简单工厂、工厂方法和抽象工厂
在前面的学习中(参见前面的博客),我们学到了很多OO原则: 封装变化 多用组合,少用继承 针对接口/超类编程,不针对实现编程 松耦合 开闭原则 让我们从一个简单的类开始,看看如何将之改造成符合OO原则 ...
- Java设计模式---工厂模式(简单工厂、工厂方法、抽象工厂)
工厂模式:主要用来实例化有共同接口的类,工厂模式可以动态决定应该实例化那一个类.工厂模式的形态工厂模式主要用一下几种形态:1:简单工厂(Simple Factory).2:工厂方法(Factory M ...
- 设计模式--简单工厂VS工厂VS抽象工厂
前几天我一直在准备大学毕业生,始终绑起来,如今,终于有时间去学习设计模式.我们研究今天的话题是植物三口之家的设计模式的控制--简单工厂VS工厂VS抽象工厂. 经过细心推敲,我们不难得出:工厂模式是简单 ...
- C#设计模式之二简单工厂模式(过渡模式)
一.引言 之所以写这个系列,是了为了自己更好的理解设计模式,也为新手提供一些帮助,我都是用最简单的.最生活化的实例来说明.在上一篇文章中讲解了单例模式,今天就给大家讲一个比较简单的模式--简单工厂模式 ...
随机推荐
- Shell and DOS
long long ago 自己便想总结下shell命令以及dos常用的命令,毕竟实际实践中会经常用到,用的好的批处理或者shell脚本会事半功倍,好了,废话不多说,开始. shell echo [字 ...
- winfrom之datagridview分页显示
这次datagridview绑定数据并分页操作,因为用到了webservice,所以代码会详细讲解.QueryByCondition是一个查询函数 客户端: PageData pageData=new ...
- Caused by: java.io.FileNotFoundException: velocity.log (No such file or directory)
Caused by: org.apache.velocity.exception.VelocityException: Error initializing log: Failed to initia ...
- 制作CSS绚烂效果的三种属性
animation(动画).transition(过渡).transform(变形) https://www.cnblogs.com/shenfangfang/p/5713564.html
- 【ZJOI2012】灾难
[ZJOI2012]灾难 阿米巴是小强的好朋友. 阿米巴和小强在草原上捉蚂蚱.小强突然想,如果蚂蚱被他们捉灭绝了,那么吃蚂蚱的小鸟就会饿死,而捕食小鸟的猛禽也会跟着灭绝,从而引发一系列的生态灾难. 学 ...
- 【CQOI2014】危桥
[CQOI2014]危桥 Description Alice和Bob居住在一个由N个岛屿组成的国家,岛屿被编号为\(0\)到\(N-1\).某些岛屿之间有桥相连,桥上的道路都是双向的,但是一次只能供一 ...
- KazaQ's Socks (找规律)
#include<iostream> using namespace std; #define ll long long ll n, m; ll t; int main(){ while ...
- element not interactable,这种提示表示元素当前在页面上不可见
1.出现element not interactable,发现这个元素在页面上不可见,需要拖动下拉框才能看到这个元素 2.这个时候需要让元素在页面上可见,才可操作
- 初学Python——RabbitMQ的安装
记录踩坑之路,本篇文章主要摘抄自CSDN博客https://blog.csdn.net/weixin_39735923/article/details/79288578 Windows10环境下安装R ...
- [Micropython] TPYBoard STM32F407开发板运行第一个脚本
从这篇教程开始将动手在TPYBoard STM32F407开发板上运行 Python 脚本,下面教大家拿到这个开发板后怎么用!(该款开发板某宝上有售) 1 连接开发板 通过 USB 线连接你的 PC ...