前面分析了简单工厂模式和工厂方法模式,接着来看一下抽象工厂模式,他与工厂方法模式有一些相似的地方,也有不同的地方。

先来看一个不用工厂方法模式实现的订购披萨的代码:

对象依赖的问题:当你直接实例化一个对象时,就是在依赖他的具体类。接着上面的例子,如果在一个PizzaStore里面直接创建很多对象时,他们的依赖关系是这样的:

这里引出一个概念:依赖倒置。很清楚的代码里减少具体类的依赖是一件好事。依赖倒置的定义是:要依赖抽象,不要依赖实现。这个原则说说明了:不能让高层组建依赖底层组件,而且,不管是高层组件还是底层组件,他们都要依赖抽象。所谓的高层组件,是由其它底层组件定义其行为的类。例如。PizzaStore是个高层组件,因为他的行为是由比萨定义的:PizzaStore创建所有不同的比萨对象,准备、烘烤、切片、装盒;而披萨本身属于低层组件。

原则的应用

上图展示的问题在于,它依赖每个比萨类型,因为它是在自己的orderPizza方法中,实例化这些具体类型的。虽然我们已经创建了一个抽象,就是Pizza,但是我们仍然在代码中,实际的创建了具体的pizza,所以,这个抽象没有什么影响力。如何在OrderPizza方法中,将这些实例化对象的代码独立出来,我们都知道,工厂方法刚好能排上用场。所以,应用工厂方法之后。类图看起来就像这:

在应用了工厂方法模式之后(参考工厂方法模式),你将注意到,高层组件和底层组件都依赖了抽象(Pizza),要遵循依赖倒置原则,工厂方法并非唯一的技巧,但是,确实最有威力的技巧之一。

如何更好的遵循依赖倒置原则

变量不可以持有具体类的引用。如果使用new,就会持有具体类的引用,就会对具体的类产生依赖。可以改用工厂来避免这种做法。

不要让类派生自具体的类。如果派生自具体类,那你就会依赖具体类。请派生自一个抽象。

不要覆盖基类中已经实现的方法。基类中已实现的方法,应该由所有的子类共享。

上面三点只是说尽量去做到,并不是一定要做到。

抽象工厂模式

这个模式涉及的类有点儿多。。。所以决定线上UML类图,然后根据类图来一步一步的说明:

实际上单就拿抽象工厂来说,不是太难说明这个模式的含义,但是抽象工厂一般是和工厂方法模式配合使用的:

看图:首先,NYPizzaStore还是继承自PizzaStore

public abstract   class PizzaStore
{
public Pizza OrderPizza(string pizzaType)
{
Pizza pizza = CreatePizza(pizzaType);
pizza.Prepare();
pizza.Bake();
pizza.Cut();
pizza.Box();
return pizza;
}
public abstract Pizza CreatePizza(string pizzaType);
}

PizzaStroe依赖一个抽象的Pizza,而NYPizzaStore继承了PizzaStore,覆写了CreatePIzza:

 public class NyPizzaStore:PizzaStore
{
public override Pizza CreatePizza(string pizzaType)
{
Pizza pizza=null;
var indigredientFactory=new NyPizzaIngredientFactory();
if (pizzaType=="Cheese")
{
pizza = new CheesePizza(indigredientFactory);
}
else if (pizzaType=="Clam")
{
pizza=new ClamPizza(indigredientFactory);
}
return pizza;
}
}

实现的这个NYPizzaStore的耦合比较高,因为他在俩面new了三个对象:一个抽象工厂NyPizzaIngredientFactory,两个具体的披萨CheesePizza和ClamPizza。但是因为这个部分应该是不易变的部分,以后也不会进行修改了(如果不是这样的话那这个类还得继续抽象不是么?)此外,NYPizzaStore还针对一个Pizza抽象进行了编程,Pizza的代码如下:

public abstract class Pizza
{
public string Name { get; set; }
public Dough Dough { get; set; }
public Sauce Sauce { get; set; }
public Veggie[] Veggies { get; set; }
public Repperoni Repperoni { get; set; }
public Clams Clams { get; set; }
public Cheese Cheese { get; set; }
public abstract void Prepare(); public void Bake()
{
Console.WriteLine("bake...");
} public void Cut()
{
Console.WriteLine("Cut...");
} public void Box()
{
Console.WriteLine("Box...");
} public override string ToString()
{
return this.Name;
}
}

Pizza里面依赖了很多的抽象,也就是说Pizza是针对抽象在编程,具体是一个什么披萨,应该交给Pizza的子类来进行详细的描述,此外,看到Pizza里面还有一个抽象的Prepare方法,由此判断出来Pizza类也实现了工厂方法模式。看一下Pizza的具体实现类(其中一个):

 public class CheesePizza : Pizza
{
private IPizzaIngredientFactory _factory; public CheesePizza(IPizzaIngredientFactory factory)
{
_factory = factory;
}
public override void Prepare()
{
Console.WriteLine($"Preparing {Name}");
Dough = _factory.CreateDough();
Sauce = _factory.CreateSauce();
Cheese = _factory.CreateCheese();
}
}

CheesePizaa针对一个抽象的IPizzaIngredientFactory进行编程,这种利用组合的方式避免了继承带来的静态的编译所造成的不便,使得代码可以在运行时体现出更灵活和可扩展的特性。IPizzaIngredientFactory是一个抽象工厂的接口,抽象工厂又叫做原料工厂,就是说生产产品所需要的各种原料都可以从抽象工厂来获取:

 public class NyPizzaIngredientFactory : IPizzaIngredientFactory
{
public Dough CreateDough()
{
return new ThinCruseDough();
} public Sauce CreateSauce()
{
return new MarinaraSauce();
} public Cheese CreateCheese()
{
return new ReggianoChesse();
} public Veggie[] CreateVeggies()
{
return new Veggie[]
{
new Garlic(), new Mushroom(), new Onion(), new RedPepper(),
};
} public Repperoni CreateRepperoni()
{
return new SlicedRepperoni();
} public Clams CreateClams()
{
return new FreshClams();
}
}

下面给出抽象工厂的定义:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。在这个示例中,这个接口指的就是IPizzaInfredientFactory。

最重要的结论是,抽象工厂并不是单独使用的,而是在工厂方法模式中进行扩展使用的。有的时候,我们要创建一些相关联的或依赖的一族对象,这个时候可以把创建这些对象用抽象工厂来实现,而不是用工厂方法模式来实现,如果用工厂方法模式来实现,一个抽象产品对应一个抽象工厂,那样的情况叫做“类型爆炸”。

最最重要的是:工厂方法模式遵循控制反转的设计原则,并在此基础上定义了一个框架,在框架中,如果又必要的话,将抽象工厂的逻辑放到这个框架中的适当位置上。所以,工厂方法模式和抽象工厂模式一般是配合使用的。抽象工厂的作用就是防止"类型爆炸”。

要点:

所有工厂都是用来封装对象的创建。

简单工厂提供给我们更多的是一种想法,让我们萌生了将系统做一些分隔的想法,比如将对象的使用和实现进行解耦,等等。

工厂方法使用的是继承和多态,吧对象的创建委托给子类。

抽象工厂使用的是对象的组合。对象的创建被实现在工厂接口所暴露的方法中来,比如Pizza类就实现工厂方法模式,里面的Prepare就是一个工厂方法。

所有工厂模式都是通过减少应用程序和类之间的依赖促进耦合。

依赖倒置原则,指导我们要尽量避免依赖具体,而要依赖抽象。

工厂是很有威力的技巧,帮我们尽量针对接口编程,而不是针对具体。

C#设计模式之6:抽象工厂模式的更多相关文章

  1. 大话设计模式C++版——抽象工厂模式

    前面说过,简单工厂模式是最基础的一种设计模式,那以工厂命名的设计模式就是23种设计模式中最多的一种,他们一脉相承,一步一步进化而来,这里就是其中的最后一种——抽象工厂模式(Abstract Facto ...

  2. JAVA常用设计模式(一、抽象工厂模式)

    抽象工厂模式 抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂.该超级工厂又称为其他工厂的工厂.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最 ...

  3. 设计模式学习心得<抽象工厂模式 Abstract Factory>

    抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂.该超级工厂又称为其他工厂的工厂.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 在抽 ...

  4. C#设计模式系列:抽象工厂模式(AbstractFactory)

    出自:http://www.cnblogs.com/libingql/archive/2012/12/09/2809754.html 1. 抽象工厂模式简介 1.1 定义 抽象工厂(Abstract ...

  5. [Python编程实战] 第一章 python的创建型设计模式1.1抽象工厂模式

    注:关乎对象的创建方式的设计模式就是“创建型设计模式”(creational design pattern) 1.1 抽象工厂模式 “抽象工厂模式”(Abstract Factory Pattern) ...

  6. IOS设计模式浅析之抽象工厂模式(Abstract Factory)

    概述 在前面两章中,分别介绍了简单工厂模式和工厂方法模式,我们知道简单工厂模式的优点是去除了客户端与具体产品的依赖,缺点是违反了“开放-关闭原则”:工厂方法模式克服了简单工厂模式的缺点,将产品的创建工 ...

  7. 大话设计模式Python实现- 抽象工厂模式

    抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的类 下面是一个抽象工厂的demo: #!/usr/bin/env pyth ...

  8. 【java设计模式】-03抽象工厂模式

    抽象工厂 简述 抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类.在抽象工厂模式中,接口是负责创建一个相关对象的工厂 ...

  9. 一天一个设计模式——Abstract Factory抽象工厂模式

    一.模式说明 前面学习了工厂方法(Factory Method)模式.在工厂方法模式中,在工厂方法模式中,父类决定如何生成实例,但并不决定所要生成的具体类,具体的处理交由子类来处理.这里学习的抽象工厂 ...

  10. 重学 Java 设计模式:实战抽象工厂模式

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获!

随机推荐

  1. Python的datetime模块分析

    datetime模块用于是date和time模块的合集,datetime有两个常量,MAXYEAR和MINYEAR,分别是9999和1. datetime模块定义了5个类,分别是 1.datetime ...

  2. 【Linux基础】Linux更改系统IP

    1.SUSE系统 (1)设置IP cd /etc/sysconfig/network/ vi ifcfg-eth0 BOOTPROTO='static'   #静态IPBROADCAST='192.1 ...

  3. Nginx使用教程(四):提高Nginx网络吞吐量之buffers优化

    请求缓冲区在NGINX请求处理中起着重要作用. 在接收到请求时,NGINX将其写入这些缓冲区. 这些缓冲区中的数据可作为NGINX变量使用,例如$request_body. 如果缓冲区与请求大小相比较 ...

  4. 不同主机的docker容器互相通信

    Docker启动时,会在宿主主机上创建一个名为docker0的虚拟网络接口,默认选择172.17.0.1/16,一个16位的子网掩码给容器提供了 65534个IP地址. docker0只是一个在绑定到 ...

  5. Servlet工作原理解析(tomcat7、嵌入式服务器)

      目录 Servlet 容器Tomcat Servlet 容器的启动过程 Web 应用的初始化工作 Servlet 体系结构 创建 Servlet 对象(如何被加载) 初始化 Servlet(如何被 ...

  6. Jenkins+Ansible+Gitlab自动化部署三剑客-gitlab本地搭建

    实际操作 准备linux初始环境 关闭防火墙 systemctl stop firewalld 开机自己关闭 systemctl disable firewalld 设置安全配置 为关闭 vim /e ...

  7. [CQOI2014]和谐矩阵

    嘟嘟嘟 遇到这种(看似)构造的题,我好像一般都做不出来-- 然而这题正解是高斯消元解异或方程组-- 首先我们容易列出式子a[i][j] ^ a[i - 1][j] ^ a[i + 1][j] ^ a[ ...

  8. 【Flask】abort和errorhandler、app_errorhandler进行请求中断及自定义异常处理

    在view函数中,如果需要中断request,可以使用abort(500)或者直接raise exception.当然我们还需要返回一个出错信息给前端,所以需要定制一下ErrorHandler.一般只 ...

  9. Generative Adversarial Nets[Vanilla]

    引言中已经较为详细的介绍了GAN的理论基础和模型本身的原理.这里主要是研读Goodfellow的第一篇GAN论文. 0. 对抗网络 如引言中所述,对抗网络其实就是一个零和游戏中的2人最小最大游戏,主要 ...

  10. UVA12298 Super Poker II

    怎么又是没人写题解的UVA好题,个人感觉应该是生成函数的大板子题了. 直接做肯定爆炸,考虑来一发优化,我们记一个多项式,其中\(i\)次项的系数就表示对于\(i\)这个数有多少种表示方式. 那么很明显 ...