1. 简单工厂

1. 你开了一家披萨店,点披萨的方法可能是这样:

    public Pizza orderPizza(String type) {
Pizza pizza;
if (type.equals("芒果披萨")){
pizza = new MangoPizza();
}else if (type.equals("核桃披萨")){
pizza = new WalnutPizza();
}else if (type.equals("橙皮披萨")){
pizza = new FlavedoPizza();
}else {
pizza = new StarPizza();
} pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}

可以看到,每当你想增加一种披萨类型,就要修改代码,添加一种if else条件.当有多个系统存在orderPizza的需求时,每个系统都要同时修改他们的代码.因此,需要将这种实例化具体对象的代码封装起来.

public class PizzaStore {

    SimplePizzaFactory factory;

    public PizzaStore(SimplePizzaFactory factory){
this.factory = factory;
} public Pizza orderPizza(String type) {
Pizza pizza;
pizza = factory.createPizza(type); pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
public class SimplePizzaFactory {

    public Pizza createPizza(String type){
Pizza pizza = null; if (type.equals("芒果披萨")){
pizza = new MangoPizza();
}else if (type.equals("核桃披萨")){
pizza = new WalnutPizza();
}else if (type.equals("橙皮披萨")){
pizza = new FlavedoPizza();
}else {
pizza = new StarPizza();
} return pizza;
}
}

这就是简单工厂方法,他不算一种设计模式,而是一种编程习惯.其要点有二:

1. 将产品定义为抽象类,可以被覆盖

2. 将具体的产品放到工厂类中来实例化,由工厂创建并返回给客户,这样便使得产品的创建逻辑可以解耦合,并增加了复用性

2. 工厂方法

1. 假设你现在有了很多加盟店,每种加盟店根据地区的差异有自己的工厂,但加盟店虽然采用工厂方法创建披萨,但其他部分却有所不同:烘烤的做法,不切片,使用杂七杂八的盒子...

这时,你想把创建披萨的方法与每家披萨店绑定在一起,让每家PizzaStore中都使用createPizza()创建pizza,并且这些店还可以拥有一定的制作披萨的自主权.因此,我们需要把PizzaStore和createPizza()定义成抽象的

/**
* 抽象的PizzaStore
*/
public abstract class PizzaStore { public Pizza orderPizza(String type) {
Pizza pizza;
// 使用自己的方法做披萨
pizza = createPizza(type); pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
} // 将做披萨的工厂方法放到PizzaStore中,工厂方法是抽象的,所以依赖子类来处理对象的创建
public abstract Pizza createPizza(String type);
}

2. 现在由PizzaStore的子类来具体卖披萨

public class BeijingPizzaStore extends PizzaStore {
@Override
public Pizza createPizza(String item) {
if (item.equals("橙皮披萨")){
return new FlavedoPizza();
}else {
return new PekingDuckPizza();
}
}
}
public class ChengduPizza extends PizzaStore {
@Override
public Pizza createPizza(String type) {
if (type.equals("芒果披萨")){
return new MangoPizza();
}else {
return new StarPizza();
}
}
}

现在,orderPizza()与具体的Pizza实现了一定的解耦合,决定最终Pizza对象的变成了某个具体的pizza店,而pizza店则由顾客来决定

3. 一些对工厂方法的理解:

1. 工厂方法是抽象的,所以依赖子类来创建对象,这样便将超类的代码与具体创建产品的代码分割开了

2. 工厂方法必须返回一个产品,超类通常中会用到这个产品

3. 工厂方法将产品与创建者抽象为接口,使他们成为平级的存在,具体的产品创建则使用子类来交互

4. 工厂方法将产品实例化的操作推迟到子类,子类的选择则由创建者来决定

5. 简单工厂方法更趋向组合,工厂方法则利用抽象和继承

6. 简单工厂封装了对象的创建,一个工厂处理所有的产品,但对同一个产品不可变更其做法,工厂方法则更有弹性,子类通过重写可以更改产品的创建

3. 依赖倒置原则

下面是一个简单粗暴的披萨店:

/**
* 依赖具体对象的PizzaStore
*/
public class PizzaStore { public Pizza createPizza(String style, String type){
Pizza pizza = null;
if (style.equals("一元档")){
if (type.equals("芒果披萨")){
pizza = new MangoPizza();
}else if (type.equals("核桃披萨")){
pizza = new WalnutPizza();
}
}else if (style.equals("二元档")){
if (type.equals("北京烤鸭披萨")){
pizza = new PekingDuckPizza();
}else if (type.equals("橙皮披萨")){
pizza = new FlavedoPizza();
}
}else {
System.out.println("披萨卖完了");
} pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}

可以看到,这个披萨店依赖于每一个具体的披萨对象,每增加一个披萨就要修改一次代码,增加一个依赖,可以说是非常不好用了



从图中看到,高层组件直接依赖于低层组件而依赖倒置原则的的内容就是:

要依赖抽象,不要依赖具体类

这个原则说明不管高层还是低层组件都不应该依赖具体类,而应该依赖于抽象.如图所示:



与之前相比高层组件PizzaStore不在直接依赖于具体的Pizza对象,而是依赖于Pizza接口,而具体的Pizza产品则由被高层组件依赖颠倒成了依赖Pizza接口,这就是一种依赖倒置.总体来看,PizzaStore和Pizza都抽象化了,不用再依赖于具体的类型,符合依赖倒置的设计原则.

有三个指导方针可以让你更好地遵守依赖倒置原则:

  • 变量不可以持有具体类的引用
  • 不要让类派生自具体类
  • 不要覆盖基类中已覆盖的方法

4. 抽象工厂方法:创建产品家族

与普通的工厂方法比较,抽象工厂方法要显得重型得多: 工厂方法只创建一种产品,抽象工厂方法则创建一个家族的产品

1. 典型做法是创建一个工厂的接口,这个接口负责创建所有的产品家族成员,如创建一个生产调料家族的工厂:

/**
* 原料工厂的接口,实现这样的接口,可以随意组合产出各式各样的调料
* 每个原料都是一个类
*/
public interface SeasoningFactory { Chili createChili(); DouBan createDouBan(); Pepper createPepper(); Salt createSalt(); }
/**
* 郫县调料生产厂
*/
public class PiXianSeasoningFactory implements SeasoningFactory { /**
* 虎皮青椒
* @return
*/
@Override
public Chili createChili() {
return new HuPiQingJiao();
} /**
* 郫县豆瓣
* @return
*/
@Override
public DouBan createDouBan() {
return new PiXianDouBan();
} /**
* 花椒油
* @return
*/
@Override
public Pepper createPepper() {
return new HuaJiaoYou();
} /**
* 食用盐
* @return
*/
@Override
public Salt createSalt() {
return new ShiYongYan();
}
}
/**
* 灌县调料生产厂
*/
public class GuanXianSeasoningFactory implements SeasoningFactory { /**
* 虎皮青椒
* @return
*/
@Override
public Chili createChili() {
return new HuPiQingJiao();
} /**
* 老干妈豆瓣酱
* @return
*/
@Override
public DouBan createDouBan() {
return new LaoGanMaDouBanJiang();
} /**
* 花椒油
* @return
*/
@Override
public Pepper createPepper() {
return new HuaJiaoYou();
} /**
* 食用盐
* @return
*/
@Override
public Salt createSalt() {
return new ShiYongYan();
}
}

下面是抽象工厂方法的类图:



从类图中可以看出来,抽象工厂方法有一个缺点,就是当添加了一个新产品接口时,要去抽象工厂接口中添加一个方法,这会造成一些麻烦

2. 工厂方法与抽象工厂的一些对比

相同点:

  • 都是负责创建对象
  • 都能使程序解耦,将程序从创建大量具体对象的泥潭中拉出来

不同点:

  • 工厂方法用子类来创建具体对象,利用抽象和继承.抽象工厂包含了一大堆接口,利用组合的思想来创建对象
  • 工厂方法一般用来创建一种产品,抽象工厂则创建一族产品
  • 工厂方法功能简单,使用轻便.抽象工厂功能强大,使用时会创建大量的类

3. 下面使用抽象工厂生产的原料来制作披萨

/**
* 原料工厂的接口,实现这样的接口,可以随意组合产出各式各样的调料
* 每个原料都是一个类
*/
public interface SeasoningFactory { /**
* 辣椒
* @return
*/
Chili createChili(); /**
* 豆瓣
* @return
*/
DouBan createDouBan(); /**
* 花椒
* @return
*/
Pepper createPepper(); /**
* 盐
* @return
*/
Salt createSalt(); }
/**
* 豆瓣
*/
public abstract class DouBan {
public abstract String sayMyName();
}
public class PiXianDouBan extends DouBan {
@Override
public String sayMyName() {
return "郫县豆瓣";
}
}
public class LaoGanMaDouBanJiang extends DouBan {
@Override
public String sayMyName() {
return "老干妈豆瓣酱";
}
}
/**
* 郫县调料生产厂
*/
public class PiXianSeasoningFactory implements SeasoningFactory { /**
* 虎皮青椒
* @return
*/
@Override
public Chili createChili() {
return new HuPiQingJiao();
} /**
* 郫县豆瓣
* @return
*/
@Override
public DouBan createDouBan() {
return new PiXianDouBan();
} /**
* 花椒油
* @return
*/
@Override
public Pepper createPepper() {
return new HuaJiaoYou();
} /**
* 食用盐
* @return
*/
@Override
public Salt createSalt() {
return new ShiYongYan();
}
}
/**
* 灌县调料生产厂
*/
public class GuanXianSeasoningFactory implements SeasoningFactory { /**
* 虎皮青椒
* @return
*/
@Override
public Chili createChili() {
return new HuPiQingJiao();
} /**
* 老干妈豆瓣酱
* @return
*/
@Override
public DouBan createDouBan() {
return new LaoGanMaDouBanJiang();
} /**
* 花椒油
* @return
*/
@Override
public Pepper createPepper() {
return new HuaJiaoYou();
} /**
* 食用盐
* @return
*/
@Override
public Salt createSalt() {
return new ShiYongYan();
}
}
/**
* 每一个披萨都有一组抽象工厂生产的原料
*
*/
public abstract class Pizza { protected String name; /**
* 辣椒
*/
protected Chili chili; /**
* 豆瓣
*/
protected DouBan douBan; /**
* 花椒
*/
protected Pepper pepper; /**
* 盐
*/
protected Salt salt; /**
* 在这个方法中,收集抽象工厂生产的原料
*/
public abstract void prepare(); public void bake(){
System.out.println("烘烤...");
} public void cut(){
System.out.println("切片...");
} public void box() {
System.out.println("包装...");
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString(){
return "披萨制作完毕:"+name
+"\n生产原料:"+chili.sayMyName()
+"\t"+douBan.sayMyName()
+"\t"+pepper.sayMyName()
+"\t"+salt.sayMyName(); }
}
public class ChengduPizza extends Pizza{

    // 调料工厂
SeasoningFactory seasoningFactory; public ChengduPizza(SeasoningFactory seasoningFactory) {
this.name = "旧成都披萨";
this.seasoningFactory = seasoningFactory;
} @Override
public void prepare() {
System.out.println("开始准备 "+getName()+":");
chili = seasoningFactory.createChili();
douBan = seasoningFactory.createDouBan();
pepper = seasoningFactory.createPepper();
salt = seasoningFactory.createSalt();
System.out.println("以下原料准备完毕:"+chili.sayMyName()
+","+douBan.sayMyName()+","+pepper.sayMyName()+","+salt.sayMyName());
}
}
public abstract class PizzaStore {

    public abstract Pizza createPizza(String type);
}
public class StarBoundPizzaStore extends PizzaStore {
@Override
public Pizza createPizza(String type) {
Pizza pizza;
SeasoningFactory factoryA = new PiXianSeasoningFactory();
SeasoningFactory factoryB = new GuanXianSeasoningFactory(); if (type.equals("旧成都披萨")){
pizza = new ChengduPizza(factoryA);;
}else if (type.equals("新北京披萨")){
pizza = new PekingPizza(factoryB);
}else {
pizza = new ChengduPizza(factoryB);
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box(); return pizza;
} public static void main(String[] args) {
PizzaStore pizzaStore = new StarBoundPizzaStore();
Pizza pizza = pizzaStore.createPizza("旧成都披萨");
System.out.println(pizza.toString());
Pizza pizza1 = pizzaStore.createPizza("新北京披萨");
System.out.println(pizza1.toString());
}
}
结果:
开始准备 旧成都披萨:
以下原料准备完毕:虎皮青椒,郫县豆瓣,花椒油,食用盐
烘烤...
切片...
包装...
披萨制作完毕:旧成都披萨
生产原料:虎皮青椒 郫县豆瓣 花椒油 食用盐
开始准备 新北京披萨:
以下原料准备完毕:虎皮青椒,老干妈豆瓣酱,花椒油,食用盐
烘烤...
切片...
包装...
披萨制作完毕:新北京披萨
生产原料:虎皮青椒 老干妈豆瓣酱 花椒油 食用盐

HeadFirst设计模式读书笔记之工厂模式的更多相关文章

  1. HeadFirst设计模式读书笔记(4)-工厂模式

    工厂方法模式:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个.工厂方法让类把实例化推迟到子类. 所有工厂模式都用来封装对象的创建.工厂方法模式通过让子类决定该创建的对象是什么,来达到将对象 ...

  2. HeadFirst设计模式读书笔记(1)-策略模式(Strategy Pattern)

    策略模式(Strategy Pattern): 定义了了算法簇,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户端. 第一个设计原则:找出应用中可能需要变化之处,把他们独立 ...

  3. HeadFirst设计模式读书笔记之策略模式

    1. 例子 1. 做一个鸭子模拟器,里面有很多不同的鸭子,有的可以游泳,有的可以睡觉,有的可以呱呱叫,一般套路是定义一个鸭子的超类,在 超类里定义睡觉,游泳,呱呱叫的方法,再让不同的鸭子子类继承这个超 ...

  4. HeadFirst设计模式读书笔记--目录

    HeadFirst设计模式读书笔记(1)-策略模式(Strategy Pattern) HeadFirst设计模式读书笔记(2)-观察者模式(Observer Pattern) HeadFirst设计 ...

  5. headfirst设计模式(5)—工厂模式体系分析及抽象工厂模式

    先编一个这么久不写的理由 上周我终于鼓起勇气翻开了headfirst设计模式这本书,看看自己下一个设计模式要写个啥,然后,我终于知道我为啥这么久都没写设计模式了,headfirst的这个抽象工厂模式, ...

  6. Delphi 设计模式:《HeadFirst设计模式》Delphi7代码---工厂模式之简单工厂

    简单工厂:工厂依据传进的参数创建相应的产品. http://www.cnblogs.com/DelphiDesignPatterns/archive/2009/07/24/1530536.html { ...

  7. Delphi 设计模式:《HeadFirst设计模式》Delphi代码---工厂模式之抽象工厂[转]

     1  2 {<HeadFirst设计模式>工厂模式之抽象工厂 }  3 { 抽象工厂的产品                       }  4 { 编译工具:Delphi7.0     ...

  8. Delphi 设计模式:《HeadFirst设计模式》Delphi2007代码---工厂模式之工厂方法[转]

      1   2{<HeadFirst设计模式>工厂模式之工厂方法 }   3{ 产品类                              }   4{ 编译工具 :Delphi20 ...

  9. headfirst设计模式(4)—工厂模式

    开篇 天天逛博客园,就是狠不下心来写篇博客,忙是一方面,但是说忙能有多忙呢,都有时间逛博客园,写篇博客的时间都没有?(这还真不好说) 每次想到写一篇新的设计模式,我总会问自己: 1,自己理解了吗? 2 ...

随机推荐

  1. .net core Entity Framework 与 EF Core

    重点讲 Entity Framework Core ! (一)Entity Framework 它是适用于.NET 的对象关系映射程序 (ORM),现在的EF6已经是久经沙场,并经历重重磨难,获得一致 ...

  2. 《前端之路》之 JavaScript 高级技巧、高阶函数(一)

    目录 一.高级函数 1-1 安全的类型检测 1-2 作用域安全的构造函数 1-3 惰性载入函数 1-4 函数绑定 1-5 函数柯里化 1-6 反函数柯里化 一.高级函数 1-1 安全的类型检测 想到类 ...

  3. JPA中自定义的插入、更新、删除方法为什么要添加@Modifying注解和@Transactional注解?

    前几天,有个同事在使用JPA的自定义SQL方法时,程序一直报异常,捣鼓了半天也没能解决,咨询我的时候,我看了一眼他的程序,差不多是这个样子的: @Repository public interface ...

  4. 我为什么放弃MySQL?最终选择了MongoDB

    最近有个项目的功能模块,为了处理方便,需要操作集合类型的数据以及其他原因.考虑再三最终决定放弃使用MySQL,而选择MongoDB. 两个数据库,大家应该都不陌生.他们最大的区别就是MySQL为关系型 ...

  5. 基础测试jmeter5.0+badboy(从小白到入门)

    1]测试工具jmeter环境安装 1.1]安装jdk环境 1:必须安装jdk8.0(我尝试安装最新版本不行,好像当时没有配置好.之后安装8.0遍可以正常运行)下载地址:单击此段 配置jdk环境:鼠标右 ...

  6. swagger Failed to load Api definition 的问题

    这个问题是由于Tomcat乱码问题导致的,修改server.xml文件的编码格式修改成UTF-8

  7. Dynamics 365中自定义工作流活动更新了输入输出参数后获取的方法

    关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复245或者20170309可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong. ...

  8. SqlServer中循环给多张表建立聚簇索引

    缘由 因为在某个复(bian)杂(tai)需求中用到了170+张表进行查询,而且表中的数据过多,查起来缓慢.只能给这些表添加索引.但是,连表名也是无法确定的(无力吐槽). 解决方法 使用游标遍历查询出 ...

  9. 20171201 - macOS High Sierra 神级 bug

    昨日亲测有效,macOS High Sierra 神级 bug,系统管理员 root 密码为空,输入就可以登录,具备最高权限. 让人不禁想象 Apple Software 怎么了,人才都流失了吗?

  10. Linux 桌面玩家指南:12. 优秀的文本化编辑思想大碰撞(Markdown、LaTeX、MathJax)

    特别说明:要在我的随笔后写评论的小伙伴们请注意了,我的博客开启了 MathJax 数学公式支持,MathJax 使用$标记数学公式的开始和结束.如果某条评论中出现了两个$,MathJax 会将两个$之 ...