设计模式 ~ 创建型模式 ~ 工厂模式 ~ Factory Pattern。


文章目录

  • 设计模式 ~ 创建型模式 ~ 工厂模式 ~ Factory Pattern。
    • eg.
    • 简单工厂模式。
      • 结构。
      • 优点。
      • 缺点。
      • 扩展 ~ 静态工厂。
    • 工厂方法模式。
      • 结构。
      • 优点。
      • 缺点。
    • 小结。
    • 抽象工厂模式。
      • 结构。
      • 优点。
      • 缺点。
      • 抽象工厂模式实现。
      • 优点。
      • 缺点。
      • 使用场景。
    • 工厂模式小结。
      • 应用场景。
    • 模式扩展。
    • jdk 源码解析 ~ Collection.iterator(); 方法。

实现了创建者和调用者的分离。

  • 简单工厂模式。
    用来生产同一等级结构中的任意产品。(对于增加新的产品,需要修改已有代码)。

  • 工厂方法模式。
    用来生产同一等级结构中的固定产品(支持增加任意产品)。

  • 抽象工厂模式。
    围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。

  • OOP 七大原则。

开闭原则:一个元件的实体应当对扩展开放,对修改关闭。
依赖倒转原则:要针对接口编程,不要针对实现编程。
迪米特原则:只与你直接的朋友通信,避免和陌生人通信。

  • 核心本质。

实例化对象不使用 new,用工厂方法代替。
将选择实现类、创建对象统一管理和控制。从而将调用者和我们的实现类解耦。


eg.

设计一个咖啡点餐系统。

设计一个咖啡类(Coffee),并定义其两个子类(美式咖啡【Americano】和拿铁咖啡【CaffèLatte】)。
再设计一个咖啡店类(CoffeeShop),咖啡店具有点咖啡的功能。
具体类的设计如下。

package com.geek.factory.pattern.simple.before;/*** 咖啡类 ~ 抽象。** @author geek*/
public abstract class AbstractCoffee {/*** 获取名称。** @return*/protected abstract String getName();/*** 加糖。*/public void addSugar() {System.out.println("加糖。");}/*** 加奶。*/public void addMilk() {System.out.println("加奶。");}}
package com.geek.factory.pattern.simple.before;/*** 美式咖啡。** @author geek*/
public class Americano extends AbstractCoffee {/*** 获取名称。** @return*/@Overrideprotected String getName() {return "美式咖啡";}}
package com.geek.factory.pattern.simple.before;/*** 拿铁咖啡。** @author geek*/
public class CaffèLatte extends AbstractCoffee {/*** 获取名称。** @return*/@Overrideprotected String getName() {return "CaffèLatte";}}
package com.geek.factory.pattern.simple.before;/*** @author geek*/
public class CoffeeShop {public AbstractCoffee orderCoffee(String type) {// 声明 coffee 类型变量。AbstractCoffee coffee = null;if ("Americano".equals(type)) {coffee = new Americano();} else if ("CaffèLatte".equals(type)) {coffee = new CaffèLatte();} else {throw new RuntimeException("你所点的咖啡不存在。");}// 加配料。coffee.addMilk();coffee.addSugar();return coffee;}}
package com.geek.factory.pattern.simple.before;/*** @author geek*/
public class Client {public static void main(String[] args) {// 创建咖啡店。CoffeeShop coffeeShop = new CoffeeShop();// 点咖啡。AbstractCoffee coffee = coffeeShop.orderCoffee("CaffèLatte");System.out.println("coffee = " + coffee);AbstractCoffee coffee1 = coffeeShop.orderCoffee("Americano");System.out.println("coffee1 = " + coffee1);}}/*
加奶。
加糖。
CaffèLatte
加奶。
加糖。
美式咖啡Process finished with exit code 0*/

在 Java 中,万物皆对象,这些对象都需要创建,如果创建的时候直接 new 该对象,就会对该对象耦合严重,假如我们要更换对象,所有 new 对象的地方都需要修改一遍,这显然违背了软件设计的开闭原则。

如果我们使用工厂来生产对象,我们就只和工厂打交道就可以了,彻底和对象解耦,如果要更换对象,直接在工厂里更换该对象即可,达到了与对象解耦的目的。所以说,工厂模式最大的优点就是:解耦。


简单工厂模式。

结构。

简单工厂包含如下角色。

  • 抽象产品 ~ 定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品 ~ 实现或者继承抽象产品的子类。
  • 具体工厂 ~ 提供了创建产品的方法,调用者通过该方法来获取产品。

咖啡。

package com.geek.factory.pattern.simple.after;/*** 咖啡类。** @author geek*/
public abstract class AbstractCoffee {/*** 获取名称。** @return*/protected abstract String getName();/*** 加糖。*/public void addSugar() {System.out.println("加糖。");}/*** 加奶。*/public void addMilk() {System.out.println("加奶。");}}
package com.geek.factory.pattern.simple.after;/*** 美式咖啡。** @author geek*/
public class Americano extends AbstractCoffee {/*** 获取名称。** @return*/@Overrideprotected String getName() {return "美式咖啡";}}
package com.geek.factory.pattern.simple.after;/*** 拿铁咖啡。** @author geek*/
public class CaffèLatte extends AbstractCoffee {/*** 获取名称。** @return*/@Overrideprotected String getName() {return "CaffèLatte";}}
package com.geek.factory.pattern.simple.after;/*** @author geek*/
public class CoffeeShop {public AbstractCoffee orderCoffee(String type) {// 创建咖啡工厂。SimpleCoffeeFactory factory = new SimpleCoffeeFactory();// 制作咖啡。AbstractCoffee coffee = factory.createCoffee(type);// 加配料。coffee.addMilk();coffee.addSugar();return coffee;}}
package com.geek.factory.pattern.simple.after;/*** 简单咖啡工厂。** @author geek*/
public class SimpleCoffeeFactory {public AbstractCoffee createCoffee(String type) {AbstractCoffee coffee = null;if ("Americano".equals(type)) {coffee = new Americano();} else if ("CaffèLatte".equals(type)) {coffee = new CaffèLatte();} else {throw new RuntimeException("你所点的咖啡不存在。");}// 加配料。coffee.addMilk();coffee.addSugar();return coffee;}}
package com.geek.factory.simple.after;/*** @author geek*/
public class Client {public static void main(String[] args) {// 创建咖啡店。CoffeeShop coffeeShop = new CoffeeShop();// 点咖啡。Coffee coffee = coffeeShop.orderCoffee("CaffèLatte");System.out.println(coffee.getName());Coffee coffee1 = coffeeShop.orderCoffee("Americano");System.out.println(coffee1.getName());}}/*
加奶。
加糖。
CaffèLatte
加奶。
加糖。
美式咖啡Process finished with exit code 0*/

工厂(factory)处理创建对象的细节,一旦有了 SimpleCoffeeFactory,CoffeeStore 类中的 orderCoffee(); 就变成此对象的客户,后期如果需要 Coffee 对象直接从工厂中获取即可。这样也就解除了和 Coffee 实现类的耦合,同时又产生了新的耦合,CoffeeStore 对象和 SimpleCoffeeFactory 工厂对象的耦合,工厂对象和商品对象的耦合。
后期如果再加新品种的咖啡,我们势必要需求修改 SimpleCoffeeFactory 的代码,违反了开闭原则。工厂类的客户端可能有很多,比如创建美团外卖等,这样只需要修改工厂类的代码,省去其他的修改操作。

汽车。

package com.geek.factory.simple;public interface ICar {void name();}
package com.geek.factory.simple;/*** @author geek*/
public class Tesla implements ICar {@Overridepublic void name() {System.out.println("特斯拉。");}}
package com.geek.factory.simple;/*** @author geek*/
public class WuLing implements Car {@Overridepublic void name() {System.out.println("五菱宏光。");}}
package com.geek.factory.simple;/*** 简单工厂模式 / 静态工厂模式。* 开闭原则。* * @author geek*/
public class CarFactory {// 方法 1。public static ICar getCar(String car) {if ("特斯拉".equals(car)) {return new Tesla();} else if ("五菱宏光".equals(car)) {return new WuLing();} else {return null;}}// 增加一个新的产品,如果不修改代码,做不到。// ↓ ↓ ↓// 方法 2。public static ICar getWuling() {return new WuLing();}public static ICar getTesla() {return new Tesla();}}
package com.geek.factory.simple;/*** @author geek*/
public class Consumer {public static void main(String[] args) {// 自己造。
//        ICar car1 = new WuLing();
//        ICar car2 = new Tesla();// 工厂模式。花钱买。ICar car1 = CarFactory.getCar("特斯拉");ICar car2 = CarFactory.getCar("五菱宏光");car1.name();car2.name();}}

优点。

封装了创建对象的过程,可以通过参数直接获取对象。把对象的创建和业务逻辑层分开,这样以后就避免了修改客户代码,如果要实现新产品直接修改工厂类,而不需要在原代码中修改,这样就降低了客户代码修改的可能性,更加容易扩展。

用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。

在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则。


缺点。

增加新产品时还是需要修改工厂类的代码,违背了“开闭原则”。


扩展 ~ 静态工厂。

在开发中也有一部分人将工厂类中的创建对象的功能定义为静态的,这个就是静态工厂模式,ta 也不是 23 种设计模式中的。代码如下。

package com.geek.factory.pattern.staticFactory;/*** 咖啡类。** @author geek*/
public abstract class AbstractCoffee {/*** 获取名称。** @return*/protected abstract String getName();/*** 加糖。*/public void addSugar() {System.out.println("加糖。");}/*** 加奶。*/public void addMilk() {System.out.println("加奶。");}}
package com.geek.factory.pattern.staticFactory;/*** 美式咖啡。** @author geek*/
public class Americano extends AbstractCoffee {/*** 获取名称。** @return*/@Overrideprotected String getName() {return "Americano";}}
package com.geek.factory.pattern.staticFactory;/*** 拿铁咖啡。** @author geek*/
public class CaffèLatte extends AbstractCoffee {/*** 获取名称。** @return*/@Overrideprotected String getName() {return "CaffèLatte";}}
package com.geek.factory.pattern.staticFactory;/*** 静态咖啡工厂。** @author geek*/
public class SimpleCoffeeFactory {public static AbstractCoffee createCoffee(String type) {AbstractCoffee coffee = null;if ("Americano".equals(type)) {coffee = new Americano();} else if ("CaffèLatte".equals(type)) {coffee = new CaffèLatte();} else {throw new RuntimeException("你所点的咖啡不存在。");}// 加配料。coffee.addMilk();coffee.addSugar();return coffee;}}
package com.geek.factory.pattern.staticFactory;/*** @author geek*/
public class CoffeeShop {public AbstractCoffee orderCoffee(String type) {//        // 创建咖啡工厂。
//        SimpleCoffeeFactory staticFactory = new SimpleCoffeeFactory();
//        // 制作咖啡。
//        AbstractCoffee coffee = staticFactory.createCoffee(type);AbstractCoffee coffee = SimpleCoffeeFactory.createCoffee(type);// 加配料。coffee.addMilk();coffee.addSugar();return coffee;}}
package com.geek.factory.pattern.staticFactory;/*** @author geek*/
public class Client {public static void main(String[] args) {// 创建咖啡店。CoffeeShop coffeeShop = new CoffeeShop();// 点咖啡。AbstractCoffee coffee = coffeeShop.orderCoffee("CaffèLatte");System.out.println(coffee.getName());AbstractCoffee coffee1 = coffeeShop.orderCoffee("Americano");System.out.println(coffee1.getName());}}/*
加奶。
加糖。
CaffèLatte
加奶。
加糖。
美式咖啡Process finished with exit code 0*/

工厂方法模式。

定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。

结构。

工厂方法模式的主要角色。

  • 抽象工厂(Abstract Factory) 。
    提供了创建产品的接口,调用者通过 ta 访问具体工厂的工厂方法来创建产品。

  • 具体工厂(ConcreteFactory)。
    主要是实现抽象工厂中的抽象方法,完成具体产品的创建。

  • 抽象产品(Product)。
    定义了产品的规范,描述了产品的主要特性和功能。

  • 具体产品(ConcreteProduct)。
    实现了抽象产品角色所定义的接口,由具体工厂来创建,ta 同具体工厂之间一一对应。

要新增新品牌的车,new 一个车工厂。

咖啡。

package com.geek.factory.pattern.method;/*** ICoffeeFactory ~ 抽象工厂。** @author geek*/
public interface ICoffeeFactory {/*** 创建 AbstractCoffee 对象。** @return*/AbstractCoffee createCoffee();}
package com.geek.factory.pattern.method;/*** 美式咖啡工厂对象,专门生产美式咖啡。** @author geek*/
public class AmericanoFactory implements ICoffeeFactory {/*** 创建 AbstractCoffee 对象。** @return*/@Overridepublic AbstractCoffee createCoffee() {return new Americano();}}
package com.geek.factory.pattern.method;import lombok.Data;/*** 美式咖啡。** @author geek*/
@Data
public class Americano extends AbstractCoffee {/*** 获取名称。** @return*/@Overrideprotected String getName() {return "Americano";}}
package com.geek.factory.pattern.method;/*** 拿铁咖啡工厂对象,专门生产拿铁咖啡。** @author geek*/
public class CaffèLatteFactory implements ICoffeeFactory {/*** 创建 AbstractCoffee 对象。** @return*/@Overridepublic AbstractCoffee createCoffee() {return new CaffèLatte();}}
package com.geek.factory.pattern.method;import lombok.Data;/*** 拿铁咖啡。** @author geek*/
@Data
public class CaffèLatte extends AbstractCoffee {/*** 获取名称。** @return*/@Overrideprotected String getName() {return "CaffèLatte";}}
package com.geek.factory.pattern.method;/*** 咖啡类 ~ 抽象。** @author geek*/
public abstract class AbstractCoffee {/*** 获取名称。** @return*/protected abstract String getName();/*** 加糖。*/public void addSugar() {System.out.println("加糖。");}/*** 加奶。*/public void addMilk() {System.out.println("加奶。");}}
package com.geek.factory.method.coffee;/*** @author geek*/
public class CoffeeShop {private ICoffeeFactory coffeeFactory;public void setCoffeeFactory(ICoffeeFactory coffeeFactory) {this.coffeeFactory = coffeeFactory;}/*** 点咖啡。** @return*/public Coffee orderCoffee() {Coffee coffee = coffeeFactory.createCoffee();// 加配料。coffee.addMilk();coffee.addSugar();return coffee;}}
package com.geek.factory.method.coffee;/*** @author geek*/
public class Client {public static void main(String[] args) {// 创建咖啡店对象。CoffeeShop coffeeShop = new CoffeeShop();// ~ ~ ~ ~ ~ ~ ~// 创建工厂对象。AmericanoFactory factory = new AmericanoFactory();coffeeShop.setCoffeeFactory(factory);// 点咖啡。Coffee coffee = coffeeShop.orderCoffee();System.out.println("coffee = " + coffee);System.out.println(coffee.getName());// 加奶。//加糖。//coffee = com.geek.factory.method.coffee.Americano@1b6d3586//美式咖啡System.out.println("~ ~ ~ ~ ~ ~ ~");// 创建工厂对象。CaffèLatteFactory factory1 = new CaffèLatteFactory();coffeeShop.setCoffeeFactory(factory1);// 点咖啡。Coffee coffee1 = coffeeShop.orderCoffee();System.out.println("coffee = " + coffee1);System.out.println(coffee1.getName());// 加奶。//加糖。//coffee = com.geek.factory.method.coffee.CaffèLatte@4554617c//CaffèLatte}}

eg. 汽车。

package com.geek.factory.method;/*** 工厂方法模式。*/
public interface ICarFactory {ICar getCar();}
package com.geek.factory.method;/*** 工厂方法模式。*/
public interface ICarFactory {ICar getCar();}
package com.geek.factory.method;/*** @author geek*/
public class TeslaFactory implements ICarFactory {@Overridepublic ICar getCar() {return new Tesla();}}
package com.geek.factory.method;/*** @author geek*/
public class Tesla implements ICar {@Overridepublic void name() {System.out.println("特斯拉。");}}
package com.geek.factory.method;/*** @author geek*/
public class Consumer {public static void main(String[] args) {ICar wuling = new WulingFactory().getCar();ICar tesla = new TeslaFactory().getCar();ICar mobai = new MobaiFactory().getCar();wuling.name();tesla.name();mobai.name();}}

如果需要增加新车,只需扩展车的工厂类。

package com.geek.factory.method;/*** @author geek*/
public class Mobai implements ICar {@Overridepublic void name() {System.out.println("摩拜。");}}
package com.geek.factory.method;/*** @author geek*/
public class MobaiFactory implements ICarFactory {@Overridepublic ICar getCar() {return new Mobai();}}

直接从工厂拿车。(new xxxFactory();)。

package com.geek.factory.method;/*** @author geek*/
public class Consumer {public static void main(String[] args) {ICar wuling = new WulingFactory().getCar();ICar tesla = new TeslaFactory().getCar();ICar mobai = new MobaiFactory().getCar();wuling.name();tesla.name();mobai.name();}}

优点。
  • 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。

  • 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则。


缺点。

每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。


小结。

对比选择。

结构复杂度:简单工厂模式。
代码复杂度:简单工厂模式。
编程复杂度:简单工厂模式。
管理复杂度:简单工厂模式。

根据设计原则:工厂方法模式。
根据实际业务:简单工厂模式。


抽象工厂模式。

前面介绍的工厂方法模式中考虑的是一类产品的生产,如畜牧场只养动物、电视机厂只生产电视机等。

这些工厂只生产同种类产品,同种类产品称为同等级产品,也就是说:工厂方法模式只考虑生产同等级的产品,但是在现实生活中许多工厂是综合型的工厂,能生产多等级(种类) 的产品,如电器厂既生产电视机又生产洗衣机或空调,大学既有软件专业又有生物专业等。

本节要介绍的抽象工厂模式将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族,下图所示横轴是产品等级,也就是同一类产品;纵轴是产品族,也就是同一品牌的产品,同一品牌的产品产自同一个工厂。


抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类。

是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。

抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。


结构。

抽象工厂模式的主要角色如下。

  • 抽象工厂(Abstract Factory)。
    提供了创建产品的接口,ta 包含多个创建产品的方法,可以创建多个不同等级的产品。

  • 具体工厂(Concrete Factory)。
    主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。

  • 抽象产品(Product)。
    定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。

  • 具体产品(Concrete Product)。
    实现了抽象产品角色所定义的接口,由具体工厂来创建,ta 同具体工厂之间是多对一的关系。

  • 适用场景。

客户端(应用层)不依赖于产品类实例如何被创建、实现等细节。
强调一系列相关的产品对象(属于同一产品族)一起适用创建对象需要大量的重复代码。
提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现。


优点。

具体产品在应用层的代码隔离,无需关心创建的细节。
将一个系列的产品同一到一起创建。


缺点。

规定了所有可能被创建的产品的集合,产品簇中扩展新的产品困难。
增加了系统的抽象性和理解难度。

package com.geek.factory.abstract1;/*** 手机产品接口。*/
public interface IPhoneProduct {void start();void shutdown();void callup();void sendSMS();}
package com.geek.factory.abstract1;/*** 路由器产品接口。*/
public interface IRouterProduct {void start();void shutdown();void openWifi();void setting();}
package com.geek.factory.abstract1;/*** 华为手机。** @author geek*/
public class HuaweiPhone implements IPhoneProduct {@Overridepublic void start() {System.out.println("开启华为手机。");}@Overridepublic void shutdown() {System.out.println("关闭华为手机。");}@Overridepublic void callup() {System.out.println("华为打电话。");}@Overridepublic void sendSMS() {System.out.println("华为发短信。");}}
package com.geek.factory.abstract1;/*** 华为路由器。** @author geek*/
public class HuaweiRouter implements IRouterProduct {@Overridepublic void start() {System.out.println("启动华为路由器。");}@Overridepublic void shutdown() {System.out.println("关闭华为路由器。");}@Overridepublic void openWifi() {System.out.println("打开华为 Wifi。");}@Overridepublic void setting() {System.out.println("华为路由器设置。");}}
package com.geek.factory.abstract1;/*** 小米手机。** @author geek*/
public class XiaomiPhone implements IPhoneProduct {@Overridepublic void start() {System.out.println("开启小米手机。");}@Overridepublic void shutdown() {System.out.println("关闭小米手机。");}@Overridepublic void callup() {System.out.println("小米打电话。");}@Overridepublic void sendSMS() {System.out.println("小米发短信。");}}
package com.geek.factory.abstract1;/*** 小米路由器。** @author geek*/
public class XiaomiRouter implements IRouterProduct {@Overridepublic void start() {System.out.println("启动小米路由器。");}@Overridepublic void shutdown() {System.out.println("关闭小米路由器。");}@Overridepublic void openWifi() {System.out.println("打开小米 Wifi。");}@Overridepublic void setting() {System.out.println("小米路由器设置。");}}
package com.geek.factory.abstract1;/*** 抽象产品工厂。** @author geek*/
public interface IProductFactory {/*** 生产手机。** @return*/IPhoneProduct phoneProduct();/*** 生产路由器。** @return*/IRouterProduct routerProduct();}
package com.geek.factory.abstract1;/*** @author geek*/
public class HuaweiFactory implements IProductFactory {@Overridepublic IPhoneProduct phoneProduct() {return new HuaweiPhone();}@Overridepublic IRouterProduct routerProduct() {return new HuaweiRouter();}}
package com.geek.factory.abstract1;/*** @author geek*/
public class XiaomiFactory implements IProductFactory {@Overridepublic IPhoneProduct phoneProduct() {return new XiaomiPhone();}@Overridepublic IRouterProduct routerProduct() {return new XiaomiRouter();}}
package com.geek.factory.abstract1;/*** @author geek*/
public class Client {public static void main(String[] args) {System.out.println("~ ~ 小米系列产品 ~ ~");// 小米工厂。XiaomiFactory xiaomiFactory = new XiaomiFactory();IPhoneProduct iPhoneProduct = xiaomiFactory.phoneProduct();iPhoneProduct.callup();iPhoneProduct.sendSMS();IRouterProduct iRouterProduct = xiaomiFactory.routerProduct();iRouterProduct.openWifi();System.out.println("~ ~ 华为系列产品 ~ ~");// 华为工厂。HuaweiFactory huaweiFactory = new HuaweiFactory();iPhoneProduct = huaweiFactory.phoneProduct();iPhoneProduct.callup();iPhoneProduct.sendSMS();iRouterProduct = huaweiFactory.routerProduct();iRouterProduct.openWifi();}}

抽象工厂模式实现。

现咖啡店业务发生改变,不仅要生产咖啡还要生产甜点,如提拉米苏、抹茶慕斯等,要是按照工厂方法模式,需要定义提拉米苏类、抹茶慕斯类、提拉米苏工厂、抹茶慕斯工厂、甜点工厂类,很容易发生类爆炸情况。其中拿铁咖啡、美式咖啡是一个产品等级,都是咖啡;提拉米苏、抹茶慕斯也是一个产品等级;拿铁咖啡和提拉米苏是同一产品族(也就是都属于意大利风味),美式咖啡和抹茶慕斯是同一产品族(也就是都属于美式风味)。所以这个案例可以使用抽象工厂模式实现。类图如下。


如果要加同一个产品族的话,只需要再加一个对应的工厂类即可,不需要修改其他的类。

package com.geek.factory.pattern.abstract2;/*** 美式咖啡。** @author geek*/
public class Americano extends AbstractCoffee {@Overrideprotected String getName() {return "Americano";}}
package com.geek.factory.pattern.abstract2;/*** 拿铁咖啡。** @author geek*/
public class CaffèLatte extends AbstractCoffee {@Overrideprotected String getName() {return "CaffèLatte";}}
package com.geek.factory.pattern.abstract2;/*** 提拉米苏。** @author geek*/
public class Tiramisu extends AbstractDessert {/*** show。*/@Overridevoid show() {System.out.println("提拉米苏。");}}
package com.geek.factory.pattern.abstract2;/*** 抹茶慕斯。** @author geek*/
public class MatchaMousse extends AbstractDessert {/*** show。*/@Overridevoid show() {System.out.println("抹茶慕斯。");}}
package com.geek.factory.pattern.abstract2;/*** 接口 ~ 甜品工厂。** @author geek*/
public interface IDessertFactory {/*** 生产咖啡。** @return*/AbstractCoffee createCoffee();/*** 生产甜品。** @return*/AbstractDessert createDessert();}
package com.geek.factory.pattern.abstract2;/*** 美式风味甜品工厂。** @author geek*/
public class AmericanDessertFactory implements IDessertFactory {/*** 生产咖啡。** @return*/@Overridepublic AbstractCoffee createCoffee() {return new Americano();}/*** 生产甜品。** @return*/@Overridepublic AbstractDessert createDessert() {return new MatchaMousse();}}
package com.geek.factory.pattern.abstract2;/*** 意大利风味甜品工厂。** @author geek*/
public class ItalyDessertFactory implements IDessertFactory {/*** 生产咖啡。** @return*/@Overridepublic AbstractCoffee createCoffee() {return new CaffèLatte();}/*** 生产甜品。** @return*/@Overridepublic AbstractDessert createDessert() {return new Tiramisu();}}
package com.geek.factory.pattern.abstract2;/*** 抽象咖啡类。** @author geek*/
public abstract class AbstractCoffee {/*** get name。** @return*/protected abstract String getName();/*** 加糖。*/public void addSugar() {System.out.println("加糖。");}/*** 加奶。*/public void addMilk() {System.out.println("加奶。");}}
package com.geek.factory.pattern.abstract2;/*** 抽象甜品类。** @author geek*/
public abstract class AbstractDessert {/*** show。*/abstract void show();}
package com.geek.factory.pattern.abstract2;/*** @author geek*/
public class Client {public static void main(String[] args) {// 意大利风味甜品工厂。IDessertFactory italyDessertFactory = new ItalyDessertFactory();// 获取拿铁咖啡和提拉米苏甜品。AbstractCoffee coffee = italyDessertFactory.createCoffee();AbstractDessert dessert = italyDessertFactory.createDessert();System.out.println("coffee.getName() = " + coffee.getName());System.out.println("dessert = " + dessert);dessert.show();// 美式风味甜品工厂。IDessertFactory americanFactory = new AmericanDessertFactory();// 获取美式咖啡和抹茶慕斯甜品。AbstractCoffee coffee1 = americanFactory.createCoffee();AbstractDessert dessert1 = americanFactory.createDessert();System.out.println("coffee1.getName() = " + coffee1.getName());System.out.println("dessert1 = " + dessert1);dessert1.show();}}

如果要加同一个产品族的话,只需要再加一个对应的工厂类即可,不需要修改其他的类。

优点。

当一个产品族中的多个对象被设计成一起工作时,ta 能保证客户端始终只使用同一个产品族中的对象。


缺点。

当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。


使用场景。

当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。

系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。

系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。

eg. 输入法换皮肤,一整套一起换。生成不同操作系统的程序。


工厂模式小结。

  • 简单工厂模式(静态工厂模式)。
    虽然某种程度上不符合设计原则,但实际使用最多。

  • 工厂方法模式。
    不修改已有类的前提下,通过增加新的工厂类实现扩展。

  • 抽象工厂模式。
    不可以产可以增加产品族。


应用场景。

JDK 中 Calendar 的 getlnstance(); 方法。
JDBC 中的 Connection 对象的获取。
Spring 中 IOC 容器创建管理 bean 对象。
反射中 Class 对象的 newlnstance(); 方法。


模式扩展。

简单工厂 + 配置文件解除耦合。

可以通过工厂模式 + 配置文件的方式解除工厂对象和产品对象的耦合。在工厂类中加载配置文件中的全类名,并创建对象进行存储,客户端如果需要对象,直接进行获取即可。

  • 定义配置文件。

为了演示方便,我们使用 properties 文件作为配置文件,名称为 bean.properties。

Americano=com.geek.factory.pattern.config.Americano
CaffèLatte=com.geek.factory.pattern.config.CaffeLatte
package com.geek.factory.pattern.config;import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;/*** @author geek*/
public class CoffeeFactory {/*** 定义容器对象存储咖啡对象。*/private static final Map<String, AbstractCoffee> MAP = new HashMap<>();// 只需要加载一次。static {// 加载配置文件,获取配置文件中配置的全限定类名,并创建该类的对象进行存储。Properties properties = new Properties();InputStream inputStream = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");try {properties.load(inputStream);} catch (IOException e) {throw new RuntimeException(e);}// 遍历 Properties 集合对象。Set<Object> keys = properties.keySet();for (Object key : keys) {// 根据键获取值(全类名)。String className = properties.getProperty((String) key);// 通过反射创建对象。// 获取字节码对象。Class<?> clazz = null;try {clazz = Class.forName(className);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}// 创建对象。AbstractCoffee object = null;try {object = (AbstractCoffee) clazz.newInstance();} catch (InstantiationException | IllegalAccessException e) {throw new RuntimeException(e);}// 将对象存入容器。MAP.put((String) key, object);}}public static AbstractCoffee createCoffee(String name) {return MAP.get(name);}}

jdk 源码解析 ~ Collection.iterator(); 方法。


package com.geek.factory.demo.interator;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;/*** @author geek*/
public class Demo {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("令狐冲");list.add("风清扬");list.add("任我行");// 获取迭代器对象。Iterator<String> iterator = list.iterator();// 使用迭代器遍历。while (iterator.hasNext()) {String next = iterator.next();System.out.println("next = " + next);}}}

对上面的代码大家应该很熟,使用迭代器遍历集合,获取集合中的元素。而单列集合获取迭代器的方法就使用到了工厂方法模式。我们看通过类图看看结构。


Collection 接口是抽象工厂类。

ArrayList 是具体的工厂类

Iterator接口是抽象商品类。

ArrayList 类中的 Iter 内部类是具体的商品类。

在具体的工厂类中 iterator(); 方法创建具体的商品类的对象。

另:

DateFormat 类中的 getInstance(); 方法使用的是工厂模式。
Calendar 类中的 getInstance(); 方法使用的是工厂模式。

设计模式 ~ 创建型模式 ~ 工厂模式 ~ Factory Pattern。相关推荐

  1. [设计模式-创建型]抽象工厂(Abstract Factory)

    概括 名称 Abstract Factory 结构 动机 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类. 适用性 一个系统要独立于它的产品的创建.组合和表示时. 一个系统要由多个 ...

  2. 设计模式(20):创建型-抽象工厂模式(Abstract Factory)

    设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于 ...

  3. JavaScript 设计模式核⼼原理与应⽤实践 之 创建型:工厂模式·简单工厂——区分“变与不变”

    JavaScript 设计模式核⼼原理与应⽤实践 之 创建型:工厂模式·简单工厂--区分"变与不变" 先来说说构造器 在介绍工厂模式之前,为了辅助大家的理解,我想先在这儿给大家介绍 ...

  4. JavaScript 设计模式核⼼原理与应⽤实践 之 创建型:工厂模式·抽象工厂——理解“开放封闭”

    JavaScript 设计模式核⼼原理与应⽤实践 之 创建型:工厂模式·抽象工厂--理解"开放封闭" 一个不简单的简单工厂引发的命案 在实际的业务中,我们往往面对的复杂度并非数个类 ...

  5. 设计模式(创建型)之建造者模式(Builder Pattern)

    PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbob ...

  6. 【创建型】工厂模式(Factory Pattern)详解

    工厂模式分为简单工厂.工厂方法.抽象工厂模式. 这一篇说一下简单工厂.工厂方法: 简单工厂 :用来生产同一等级结构中的任意产品.(不支持拓展增加产品) 工厂方法 :用来生产同一等级结构中的固定产品.( ...

  7. python创建方法draw_【python设计模式-创建型】工厂方法模式

    工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻 ...

  8. java设计模式——创建型之建造者模式

    自大学课程初识设计模式以来,就越发觉得有必要系统学习一下设计模式. 刚好在实习前准备期间课比较少,抽出一点时间整理一下记一些笔记,复制粘贴比较多. 笔记比较适合学习过设计模式的同学. Builder ...

  9. 设计模式-创建型-抽象工厂

    #pragma once#ifndef ABSTRACTFACTORY_H #define ABSTRACTFACTORY_H // 抽象基类AbstractProductA,代表产品A 的抽象 cl ...

  10. Java设计模式之创建型:原型模式

    一.什么是原型模式: 原型模式主要用于对象的创建,使用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象.UML类图如下: 原型模式的核心是就是原型类 Prototype,Prototype ...

最新文章

  1. 背景图片等比缩放的写法background-size简写法
  2. python stock query
  3. Leetcode 173. 二叉搜索树迭代器 解题思路及C++实现
  4. boost::math::tools::luroth_expansion用法的测试程序
  5. 如何制作linux系统硬盘,教你制作Linux操作系统的Boot/Root盘
  6. excel 区间人数柱状图_Excel中,区间统计的3种技巧都不掌握,那就真的OUt了!
  7. 使用Firebase、Angular 8和ASP.NET Core 3.1保护网站
  8. 北师大版图形的旋转二教案_北师大版三年级数学下册微课精讲+课件教案试卷(文末下载)...
  9. element-ui table列表自定义表头,修改列标题样式、添加tooltip
  10. 用户故事地图(User Story Mapping)之初体验
  11. Spring Boot Admin 基于security 认证监控
  12. 运行tensorflow-datasets遇到import tensorflow.compat.v2 as tf报错ImportError: No module named tensorflow.V2
  13. 3 步完全掌握 LoRaWAN Server,让你拥有自己的物联网
  14. class path resource [applicationContext.xml] cannot be opened because it does not exis
  15. Python生成正则测试数据
  16. php快递查询接口,一次接入顺丰、京东、申通、圆通、韵达、中通、天天、百世、邮政、EMS等主流快递公司
  17. win7-64bit 安装Python3.5
  18. STM32F103标准库开发:Keil5新建STM32工程
  19. matlab课程设计报告题目,课程设计报告
  20. python3发送qq邮件_python3通过qq邮箱发送邮件以及附件

热门文章

  1. reaver使用相关
  2. 如何学习ABAQUS有限元仿真分析软件
  3. 华为手机使用百度地图SDK-无法定位/不显示图标的解决方案之一
  4. 看看国外自动化生产过程,学习学习吧!!反省反省吧!!
  5. jolog扫地机器人怎么样_扫地机器人的效果怎么样?
  6. 中国智能马桶盖市场需求分析与投资前景预测报告2022-2028年
  7. 学生党蓝牙耳机怎么选?适合realme手机的高端蓝牙耳机推荐
  8. 【FFmpeg】FFmpeg5.1在ubuntu18.04上编译
  9. 今年考计算机二级是什么版本,2019计算机二级office考哪个版本 多少分合格
  10. spring cloud gateway routes加载顺序的研究