所有的源码请参考https://gitee.com/zyxscuec/Design-pattern.git
一、创造型设计模式
一共五种
1.1 单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
单例模式的类结构图
注意:
- 1、单例类只能有一个实例。
- 2、单例类必须自己创建自己的唯一实例。
- 3、单例类必须给所有其他对象提供这一实例。
这种很常见的应用在了spring的applicationContext.xml,在Servlet中的ServletContext、ServletConfig、
1.1.1 饿汉式单例模式
Spring 中 IOC 容器 ApplicationContext 本身就是典型的饿汉式单例
饿汉式单例是在类加载的时候就立即初始化,并且创建单例对象。绝对线程安全,在线
程还没出现以前就是实例化了,不可能存在访问安全问题。
- 优点:没有加任何的锁、执行效率比较高,在用户体验上来说,比懒汉式更好。
- 缺点:类加载的时候就初始化,不管用与不用都占着空间,浪费了内存。
package com.alibaba.design.singlepattern; /** * @author zhouyanxiang * @create 2020-07-2020/7/28-15:51 * 先静态后动态 * 先属性后方法 * 先上后下 */ public class HungrySingleton { private static final HungrySingleton hungrySingleton = new HungrySingleton(); private HungrySingleton(){ } public static HungrySingleton getInstance(){ return hungrySingleton; } }
或者也可以用下面这样,在静态代码块中去实例化。
package com.alibaba.design.singlepattern; //饿汉式静态块单例 public class HungryStaticSingleton { private static final HungryStaticSingleton hungrySingleton; static { hungrySingleton = new HungryStaticSingleton(); } private HungryStaticSingleton(){ } public static HungryStaticSingleton getInstance(){ return hungrySingleton; } }
1.1.2 懒汉式单例模式
(类加载时不初始化)
(1)线程不安全的情况
创建一个最简单的懒汉式单例模式,在不加synchronized的前提下是容易出现线程不安全的状况
package com.alibaba.design.singlepattern.lazy; /** * Created by Tom. */ //懒汉式单例 //在外部需要使用的时候才进行实例化 public class LazySimpleSingleton { private LazySimpleSingleton(){} //静态块,公共内存区域 private static LazySimpleSingleton lazy = null; public /*synchronized*/ static LazySimpleSingleton getInstance(){ if(lazy == null){ lazy = new LazySimpleSingleton(); } return lazy; } }
创建线程池,开辟线程
package com.alibaba.design.singlepattern.test; import com.alibaba.design.singlepattern.lazy.LazySimpleSingleton; /** * @author zhouyanxiang * @create 2020-07-2020/7/28-16:23 */ public class ThreadSingletonTest implements Runnable{ @Override public void run() { LazySimpleSingleton singleton = LazySimpleSingleton.getInstance(); System.out.println(Thread.currentThread().getName() + " : " + singleton); } }
测试懒汉式单例模式的线程
package com.alibaba.design.singlepattern.test; import com.alibaba.design.singlepattern.lazy.LazySimpleSingleton; /** * @author zhouyanxiang * @create 2020-07-2020/7/28-16:22 */ public class LazySimpleSingletonTest { public static void main(String[] args) { System.out.println("============="); Thread t1 = new Thread(new ThreadSingletonTest()); Thread t2 = new Thread(new ThreadSingletonTest()); Thread t3 = new Thread(new ThreadSingletonTest()); Thread t4 = new Thread(new ThreadSingletonTest()); Thread t5 = new Thread(new ThreadSingletonTest()); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); System.out.println("============="); } }
正常情况下应该是每次都会是相同的实例
不过如果运行多次,也有一定的概率出现不同的实例
如图所示,出现了多个不同的单例,由此可见在多线程的情况下是容易出现多个实例的,这样是不安全的。
(2)线程安全的情况
1. 实例化的方法上加synchronized同步锁
为了解决这个不安全的状况,可以在实例化方法那里加上synchronized关键字
package com.alibaba.design.singlepattern.lazy; /** * Created by Tom. */ //懒汉式单例 //在外部需要使用的时候才进行实例化 public class LazySimpleSingleton { private LazySimpleSingleton(){} //静态块,公共内存区域 private static LazySimpleSingleton lazy = null; public synchronized static LazySimpleSingleton getInstance(){ if(lazy == null){ lazy = new LazySimpleSingleton(); } return lazy; } }
然后我开辟了100个线程随机测试,测试多次每次得到的实例对象都是同一个,解决了线程不安全的问题
package com.alibaba.design.singlepattern.test; import com.alibaba.design.singlepattern.lazy.LazySimpleSingleton; /** * @author zhouyanxiang * @create 2020-07-2020/7/28-16:22 */ public class LazySimpleSingletonTest { public static void main(String[] args) { System.out.println("============="); // Thread t1 = new Thread(new ThreadSingletonTest()); // Thread t2 = new Thread(new ThreadSingletonTest()); // Thread t3 = new Thread(new ThreadSingletonTest()); // Thread t4 = new Thread(new ThreadSingletonTest()); // Thread t5 = new Thread(new ThreadSingletonTest()); // t1.start(); // t2.start(); // t3.start(); // t4.start(); // t5.start(); for (int i = 0; i < 100; i++) { Thread t = new Thread(new ThreadSingletonTest()); t.start(); } System.out.println("============="); } }
2. 双检锁/双重校验锁(DCL,即 double-checked locking)
JDK 版本:JDK1.5 起
是否 Lazy 初始化:是
是否多线程安全:是
实现难度:较复杂
描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
getInstance() 的性能对应用程序很关键。
在私有成员变量的时候加上volatile关键字,初始化赋值null,真正的静态实例化方法中来个双重判断
package com.alibaba.design.singlepattern.lazy; /** *@author zhouyanxiang * @create 2020-07-2020/7/28-18:22 */ public class LazyDoubleCheckSingleton { private volatile static LazyDoubleCheckSingleton lazy = null; private LazyDoubleCheckSingleton(){} public static LazyDoubleCheckSingleton getInstance(){ if(lazy == null){ synchronized (LazyDoubleCheckSingleton.class){ if(lazy == null){ lazy = new LazyDoubleCheckSingleton(); //1.分配内存给这个对象 //2.初始化对象 //3.设置lazy指向刚分配的内存地址 //4.初次访问对象 } } } return lazy; } }
3. 登记式/静态内部类
是否 Lazy 初始化:是
是否多线程安全:是
实现难度:一般
描述:这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟 饿汉式单例模式方式不同的是:饿汉式单例模式方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。想象一下,如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比饿汉式单例模式 方式就显得很合理。
public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){ } public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }
4. 效率很高且线程安全的实现方式
package com.alibaba.design.singlepattern.lazy; /** * @author zhouyanxiang * @create 2020-07-2020/7/28-17:51 */ //懒汉式单例 //这种形式兼顾饿汉式的内存浪费,也兼顾synchronized性能问题 //完美地屏蔽了这两个缺点 //史上最牛B的单例模式的实现方式 public class LazyInnerClassSingleton { //默认使用LazyInnerClassGeneral的时候,会先初始化内部类 //如果没使用的话,内部类是不加载的 private LazyInnerClassSingleton(){ if(LazyHolder.LAZY != null){ throw new RuntimeException("不允许创建多个实例"); } } //每一个关键字都不是多余的 //static 是为了使单例的空间共享 //保证这个方法不会被重写,重载 public static final LazyInnerClassSingleton getInstance(){ //在返回结果以前,一定会先加载内部类 return LazyHolder.LAZY; } //默认不加载 private static class LazyHolder{ private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton(); } }
1.1.3 枚举
JDK 版本:JDK1.5 起
是否 Lazy 初始化:否
是否多线程安全:是
实现难度:易
描述:这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。不能通过 reflection attack 来调用私有构造方法。
package com.alibaba.design.singlepattern; /** * @author zhouyanxiang * @create 2020-07-2020/7/28-21:37 */ public enum EnumSingleton { Instance; public void doSomeThing(){ System.out.println("Something has been done"); } }
单例模式小结
单例模式可以保证内存里只有一个实例,减少了内存开销;可以避免对资源的多重占用。
单例模式看起来非常简单,实现起来其实也非常简单。
经验之谈:一般情况下,不建议使用线程不安全的和直接在实例化上加上synchronized关键字的懒汉方式,建议使用饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 3 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用枚举方式。如果有其他特殊的需求,可以考虑使用第 2 种双检锁方式。
1.2 工厂模式
1.2.1 简单工厂模式
(1)概念
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
(2)适用场景
我们明确地计划不同条件下创建不同实例时使用工厂模式。
使用场景: 1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,“POP3”、“IMAP”、“HTTP”,可以把这三个作为产品类,共同实现一个接口。
**注意事项:**作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
(3)代码示例
整体类图
首先创建抽象类交通工具类:Vehicle
package com.alibaba.design.factorypattern.simplefactory; /** * @author zhouyanxiang * @create 2020-07-2020/7/28-9:49 */ public abstract class Vehicle { private String name; public Vehicle(String name) { this.name = name; System.out.println(name); } @Override public String toString(){ return "Vehicle" + name; } abstract public Vehicle newInstance(); }
然后分别创建三个实体工具类Car、Bike、Truck
- Car
package com.alibaba.design.factorypattern.simplefactory; /** * @author zhouyanxiang * @create 2020-07-2020/7/28-9:50 */ public class Car extends Vehicle { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Car(String name) { super(name); System.out.println(name); } @Override public String toString(){ return "Car:" + name; } @Override public Vehicle newInstance() { return new Car("Car ..."); } }
- Bike
package com.alibaba.design.factorypattern.simplefactory; /** * @author zhouyanxiang * @create 2020-07-2020/7/28-10:06 */ public class Bike extends Vehicle { public Bike(String name) { super(name); } @Override public Vehicle newInstance() { return new Bike("Bike ..."); } }
- Truck
package com.alibaba.design.factorypattern.simplefactory; /** * @author zhouyanxiang * @create 2020-07-2020/7/28-10:07 */ public class Truck extends Vehicle { public Truck(String name) { super(name); } @Override public Vehicle newInstance() { return new Truck("Truck ..."); } }
- 创建交通工具工厂类VehicleFactory
避免反射机制,使用注册新Vehicle类的类似工厂类,不再将类添加到map对象中,而是将要注册得到每种对象实例添加其中。每个产品类都能够创造自己的实例。
package com.alibaba.design.factorypattern.simplefactory; import java.util.HashMap; import java.util.Map; /** * @author zhouyanxiang * @create 2020-07-2020/7/28-9:47 */ public class VehicleFactory { private Map<String,Vehicle> registeredProducts = new HashMap<>(); public Vehicle createVehicle(String vehicleName){ return registeredProducts.get(vehicleName).newInstance(); } public void registerVehicle(String vehicleName,Vehicle vehicle){ registeredProducts.put(vehicleName,vehicle); }
- 客户端测试类
package com.alibaba.design.factorypattern.simplefactory; /** * @author zhouyanxiang * @create 2020-07-2020/7/28-9:54 */ public class Test { public static void main(String[] args) { Vehicle vehicle = new Vehicle("A Car") { @Override public Vehicle newInstance() { return new Car(" audi "); } }; // Vehicle vehicle2 = new Car("A Car"); // System.out.println(vehicle.toString()); // System.out.println(vehicle2.toString()); } }
(4)模式在源码中的体现
在JDK源码中 ,java.util.Calendar使用了工厂模式的简单工厂模式
public static Calendar getInstance(TimeZone zone, Locale aLocale) { return createCalendar(zone, aLocale); } private static Calendar createCalendar(TimeZone zone,Locale aLocale){ CalendarProvider provider = LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale) .getCalendarProvider(); if (provider != null) { try { return provider.getInstance(zone, aLocale); } catch (IllegalArgumentException iae) { // fall back to the default instantiation } } Calendar cal = null; if (aLocale.hasExtensions()) { String caltype = aLocale.getUnicodeLocaleType("ca"); if (caltype != null) { switch (caltype) { case "buddhist": cal = new BuddhistCalendar(zone, aLocale); break; case "japanese": cal = new JapaneseImperialCalendar(zone, aLocale); break; case "gregory": cal = new GregorianCalendar(zone, aLocale); break; } } } if (cal == null) { // If no known calendar type is explicitly specified, // perform the traditional way to create a Calendar: // create a BuddhistCalendar for th_TH locale, // a JapaneseImperialCalendar for ja_JP_JP locale, or // a GregorianCalendar for any other locales. // NOTE: The language, country and variant strings are interned. if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") { cal = new BuddhistCalendar(zone, aLocale); } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja" && aLocale.getCountry() == "JP") { cal = new JapaneseImperialCalendar(zone, aLocale); } else { cal = new GregorianCalendar(zone, aLocale); } } return cal; }
(5)简单工厂模式的优缺点
- 优点:
1、一个调用者想创建一个对象,只要知道其名称就可以了。
2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
3、屏蔽产品的具体实现,调用者只关心产品的接口。 - **缺点:**每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
1.2.2 抽象工厂模式
(1) 概念
抽象工厂模式,对方法工厂模式进行抽象。
(2)适用场景
当需要创建的对象是一系列相互关联或相互依赖的产品族时,便可以使用抽象工厂模式。说的更明白一点,就是一个继承体系中,如果存在着多个等级结构(即存在着多个抽象类),并且分属各个等级结构中的实现类之间存在着一定的关联或者约束,就可以使用抽象工厂模式。假如各个等级结构中的实现类之间不存在关联或约束,则使用多个独立的工厂来对产品进行创建,则更合适一点。
(3)代码实例
类图
还是通过交通工具这一事例来讲述,
Vehicle接口,交通工具具有出行的作用,有一个toTravel()方法
package com.alibaba.design.factorypattern.factorymethod; /** * @author zhouyanxiang * @create 2020-07-2020/7/28-10:32 */ public interface Vehicle { void toTravel(); }
抽象的自行车类实现Vehicle接口
package com.alibaba.design.factorypattern.factorymethod; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-18:20 */ public abstract class Bike implements Vehicle { public abstract void toTravel(); }
抽象的汽车类实现Vehicle接口
package com.alibaba.design.factorypattern.factorymethod; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-18:19 */ public abstract class Car implements Vehicle { public abstract void toTravel(); }
实际的ChinaBikeFactory继承Bike类,重写出行toTravel()方法
package com.alibaba.design.factorypattern.factorymethod; /** * @author zhouyanxiang * @create 2020-07-2020/7/28-10:55 */ public class ChinaBikeFactory extends Bike { @Override public void toTravel() { System.out.println("In China,I will Choose bike to travel"); } }
实际的ChinaCarFactory继承Bike类,重写出行toTravel()方法
package com.alibaba.design.factorypattern.factorymethod; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-20:05 */ public class ChinaCarFactory extends Car{ @Override public void toTravel() { System.out.println("In China,I will choose car to travel"); } }
实际的EnglandBikeFactory继承Bike类,重写出行toTravel()方法
package com.alibaba.design.factorypattern.factorymethod; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-18:36 */ public class EnglandBikeFactory extends Bike { @Override public void toTravel() { System.out.println("In England,I will Choose bike to travel"); } }
实际的EnglandCarFactory继承Bike类,重写出行toTravel()方法
package com.alibaba.design.factorypattern.factorymethod; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-20:06 */ public class EnglandCarFactory extends Car { @Override public void toTravel() { System.out.println("In England,I will choose car to travel"); } }
VehicleFactory接口,用来实例化两种交通工具的出行方法
package com.alibaba.design.factorypattern.factorymethod; /** * @author zhouyanxiang * @create 2020-07-2020/7/28-10:25 */ public interface VehicleFactory { // 实例化单车出行模式 public Vehicle bikeToTravel(); // 实例化小汽车出行模式 public Vehicle carToTravel(); }
ChinaFactory实现VehicleFactory接口
package com.alibaba.design.factorypattern.factorymethod; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-20:07 */ public class ChinaFactory implements VehicleFactory { @Override public Vehicle bikeToTravel() { return new ChinaBikeFactory(); } @Override public Vehicle carToTravel() { return new ChinaCarFactory(); } }
EnglandFactory实现VehicleFactory接口
package com.alibaba.design.factorypattern.factorymethod; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-20:08 */ public class EnglandFactory implements VehicleFactory { @Override public Vehicle bikeToTravel() { return new EnglandBikeFactory(); } @Override public Vehicle carToTravel() { return new EnglandCarFactory(); } }
运行结果
(4)模式在源代码中的体现
在java.sql.Connection接口中
Connection接口就是一个抽象工厂接口,描述了不同产品等级Statement、PreparedStatement和CallableStatement,它们都位于抽象接口Statement产品等级结构中。
从MySQL(产品族)的Connection中获取的Statement、PreparedStatement和CallableStatement肯定都是MySQL,因为他们都是一个产品族,而从SQL Server中获取的肯定都是SQL Server对应的sql执行器。
假如以后又有新的数据库出现,想在Java中使用它就需要扩展产品族并实现相关接口即可,并不需要修改原有的接口。
参考:
https://blog.csdn.net/qq_23633427/article/details/107304244
(5)抽象工厂模式的优缺点
- 优点
抽象工厂模式除了具有工厂方法模式的优点外,最主要的优点就是可以在类的内部对产品族进行约束。所谓的产品族,一般或多或少的都存在一定的关联,抽象工厂模式就可以在类内部对产品族的关联关系进行定义和描述,而不必专门引入一个新的类来进行管理。
- 缺点
产品族的扩展将是一件十分费力的事情,假如产品族中需要增加一个新的产品,则几乎所有的工厂类都需要进行修改。所以使用抽象工厂模式时,对产品等级结构的划分是非常重要的。
1.2.3 工厂方法模式
(1)概念
工厂方法模式(FACTORY METHOD)是一种常用的类创建型设计模式,此模式的核心精神是封装类中变化的部分,提取其中个性化善变的部分为独立类,通过依赖注入以达到解耦、复用和方便后期维护拓展的目的。它的核心结构有四个角色,分别是抽象工厂;具体工厂;抽象产品;具体产品
可以看做是简单工厂模式的升级版;工厂方法模式就是一个工厂接口和多个工厂实现类,要增加一个新的产品,增加一个新的工厂实现类即可,针对之前的老的工厂实现类也不需要修改。
工厂方法模式相当于在简单工厂模式的基础上,增加了对于不同的产品进行多个不同工厂的实现类的添加,不同的工厂用于Get不同的产品,用于进行不同产品的具体生产。
Client进行调用的时候,直接通过识别不同工厂,然后通过工厂接口类提供的公共方法,即可进行接口方法调用,获取产品;还需要知道具体的产品接口,用于进行具体的产品信息的获取。
(2)适用场景
目标可以无限扩展,工厂类也要随之扩展,一对一存在,满足了开闭原则,但如果目标实现较多,工厂实现类也会增多,不简洁。
MyBatis中使用的比较多,事务模块和数据源模块都使用了工厂方法模式。
(3)代码实例
整体类图:
简单工厂模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改。假如增加其他品牌交通工具,工厂类需要修改,如何解决?就用到工厂方法模式,创建一个工厂接口和创建多个工厂实现类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。
package com.alibaba.design.factorypattern.factorymothed; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-21:45 */ public interface Factory { public Vehicle toTravel(); }
交通工具接口Vehicle继承工厂接口
package com.alibaba.design.factorypattern.factorymothed; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-21:43 */ public interface Vehicle extends Factory{ public void toTravelInfo(); }
CarFactory继承Factory
package com.alibaba.design.factorypattern.factorymothed; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-21:48 */ public class CarFactory implements Factory { @Override public Vehicle toTravel() { return new Car(); } }
BikeFactory继承Factory
package com.alibaba.design.factorypattern.factorymothed; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-21:49 */ public class BikeFactory implements Factory { @Override public Vehicle toTravel() { return new Bike(); } }
Bike继承BikeFactory并且实现Vehicle接口
package com.alibaba.design.factorypattern.factorymothed; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-21:54 */ public class Bike extends BikeFactory implements Vehicle { @Override public void toTravelInfo() { System.out.println("Choose bike to travel"); } }
Car继承CarFactory并且实现Vehicle接口
package com.alibaba.design.factorypattern.factorymothed; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-21:55 */ public class Car extends CarFactory implements Vehicle { @Override public void toTravelInfo() { System.out.println("Choose car to travel "); } }
客户端测试类
package com.alibaba.design.factorypattern.factorymothed; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-21:57 */ public class Test { public static void main(String[] args) { Factory bikeFactory = new BikeFactory(); bikeFactory.toTravel().toTravelInfo(); System.out.println("=================="); Factory carFactory = new CarFactory(); carFactory.toTravel().toTravelInfo(); } }
最后输出结果
(4)工厂方法模式的在源码中的体现(在Android源码体现的比较多)
在Android源码中,ListActivity继承自Activity,将Activity作为工厂方法,生成具有ListView特点的Activity,对ListActivity的说明如下:
参考文章:https://www.cnblogs.com/yemeishu/archive/2013/01/08/2850586.html
(5)工厂方法模式的优缺点
满足了OCP(Open-Closed Principle)开闭原则,增加新的类需要修建新的工厂,增加了代码量。
如果同时需要修改多个工厂类的时候会很麻烦,而简单工厂模式只需要修改一个类,工厂方法模式是升级版的简单工厂模式。
1.3 建造者模式
(1)概念
建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
(2)适用场景
1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。
**注意事项:**与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。
(3)代码示例
我们假设一个快餐店的商业案例,其中,一个典型的套餐可以是一个汉堡(Burger)和一杯冷饮(Cold drink)。汉堡(Burger)可以是素食汉堡(Veg Burger)或鸡肉汉堡(Chicken Burger),它们是包在纸盒中。冷饮(Cold drink)可以是可口可乐(coke)或百事可乐(pepsi),它们是装在瓶子中。
我们将创建一个表示食物条目(比如汉堡和冷饮)的 Item 接口和实现 Item 接口的实体类,以及一个表示食物包装的 Packing 接口和实现 Packing 接口的实体类,汉堡是包在纸盒中,冷饮是装在瓶子中。
然后我们创建一个 Meal 类,带有 Item 的 ArrayList 和一个通过结合 Item 来创建不同类型的 Meal 对象的 MealBuilder。BuilderPatternDemo,我们的演示类使用 MealBuilder 来创建一个 Meal。
整体类图:
创建一个表示食物条目和食物包装的接口。
- Item
package com.alibaba.design.builderpattern; /** * @author zhouyanxiang * @create 2020-07-2020/7/31-10:45 */ public interface Item { public String name(); public Packing packing(); public float price(); }
- Packing
package com.alibaba.design.builderpattern; /** * @author zhouyanxiang * @create 2020-07-2020/7/31-10:46 */ public interface Packing { public String pack(); }
创建实现 Packing 接口的实体类。
- Wrapper
package com.alibaba.design.builderpattern; /** * @author zhouyanxiang * @create 2020-07-2020/7/31-10:46 */ public class Wrapper implements Packing{ @Override public String pack() { return "Wrapper"; } }
- Bottle
package com.alibaba.design.builderpattern; /** * @author zhouyanxiang * @create 2020-07-2020/7/31-10:47 */ public class Bottle implements Packing { @Override public String pack() { return "Bottle"; } }
创建实现 Item 接口的抽象类,该类提供了默认的功能。
- Burger
package com.alibaba.design.builderpattern; /** * @author zhouyanxiang * @create 2020-07-2020/7/31-10:47 */ public abstract class Burger implements Item { public Packing packing(){ return new Wrapper(); } public abstract float price(); }
- ColdDrink
package com.alibaba.design.builderpattern; /** * @author zhouyanxiang * @create 2020-07-2020/7/31-10:48 */ public abstract class ColdDrink implements Item { @Override public Packing packing() { return new Bottle(); } @Override public abstract float price(); }
创建扩展了 Burger 和 ColdDrink 的实体类。
- ChickenBurger
package com.alibaba.design.builderpattern; /** * @author zhouyanxiang * @create 2020-07-2020/7/31-10:52 */ public class ChickenBurger extends Burger { @Override public String name() { return "ChickenBurger"; } @Override public float price() { return 50.5f; } }
- VegBurger
package com.alibaba.design.builderpattern; /** * @author zhouyanxiang * @create 2020-07-2020/7/31-10:51 */ public class VegBurger extends Burger { @Override public String name() { return "VegBurger"; } @Override public float price() { return 25.0f; } }
- Coke
package com.alibaba.design.builderpattern; /** * @author zhouyanxiang * @create 2020-07-2020/7/31-10:52 */ public class Coke extends ColdDrink { @Override public String name() { return "Coke"; } @Override public float price() { return 30.0f; } }
- Pepsi
package com.alibaba.design.builderpattern; /** * @author zhouyanxiang * @create 2020-07-2020/7/31-10:53 */ public class Pepsi extends ColdDrink { @Override public String name() { return "Pepsi"; } @Override public float price() { return 35.5f; } }
创建一个 Meal 类,带有上面定义的 Item 对象。
package com.alibaba.design.builderpattern; import java.util.ArrayList; import java.util.List; /** * @author zhouyanxiang * @create 2020-07-2020/7/31-10:56 */ public class Meal { private List<Item> items = new ArrayList<Item>(); public void addItem(Item item){ items.add(item); } public float getCost(){ float cost = 0.0f; for (Item item : items) { cost += item.price(); } return cost; } public void showItems(){ for (Item item : items) { System.out.print("Item : "+item.name()); System.out.print(", Packing : "+item.packing().pack()); System.out.println(", Price : "+item.price()); } } }
创建一个 MealBuilder 类,实际的 builder 类负责创建 Meal 对象。
package com.alibaba.design.builderpattern; /** * @author zhouyanxiang * @create 2020-07-2020/7/31-10:57 */ public class MealBuilder { public Meal prepareVegMeal (){ Meal meal = new Meal(); meal.addItem(new VegBurger()); meal.addItem(new Coke()); return meal; } public Meal prepareNonVegMeal (){ Meal meal = new Meal(); meal.addItem(new ChickenBurger()); meal.addItem(new Pepsi()); return meal; } }
客户端测试类
package com.alibaba.design.builderpattern; /** * @author zhouyanxiang * @create 2020-07-2020/7/31-11:00 */ public class Test { public static void main(String[] args) { MealBuilder mealBuilder = new MealBuilder(); Meal vegMeal = mealBuilder.prepareVegMeal(); System.out.println("VegMeal"); vegMeal.showItems(); System.out.println("Total Cost: " +vegMeal.getCost()); Meal nonVegMeal = mealBuilder.prepareNonVegMeal(); System.out.println("\n\nNon-Veg Meal"); nonVegMeal.showItems(); System.out.println("Total Cost: " +nonVegMeal.getCost()); } }
输出结果
(4)在源码中的体现
在StringBuilder类中的源码
@Override public StringBuilder append(String str) { super.append(str); return this; }
再看看BeanDefinitionBuilder类
来到setFactoryMethod方法,我们写的第二种与其类似,就是典型的建造者模式
MyBatis中的SqlSessionFactoryBuilder类,也是用了建造者模式,返回SqlSessionFactory对象
看其中一个build方法
public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
可以看到在parseConfiguration方法的参数就是一个根节点,内部就是各个组件的装配流程
至此可以知道XMLConfigBuilder创建复杂对象的Configuration,而SqlSessionFactoryBuilder只不过是对XMLConfigBuilder做了一层简单的封装,用建造者来包装一层建造者
再来看看SqlSessionManager类,可以明显看出多个newInnetstance重载函数的就调用了SqlSessionFactoryBuilder建造者来创建复杂对象
public class SqlSessionManager implements SqlSessionFactory, SqlSession { private final SqlSessionFactory sqlSessionFactory; private final SqlSession sqlSessionProxy; private ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<SqlSession>(); private SqlSessionManager(SqlSessionFactory sqlSessionFactory) { this.sqlSessionFactory = sqlSessionFactory; this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance( SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionInterceptor()); } public static SqlSessionManager newInnetstance(Reader reader) { return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, null, null)); } public static SqlSessionManager newInstance(Reader reader, String environment) { return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, environment, null)); } public static SqlSessionManager newInstance(Reader reader, Properties properties) { return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, null, properties)); } public static SqlSessionManager newInstance(InputStream inputStream) { return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, null, null)); } public static SqlSessionManager newInstance(InputStream inputStream, String environment) { return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, environment, null)); } public static SqlSessionManager newInstance(InputStream inputStream, Properties properties) { return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, null, properties)); } public static SqlSessionManager newInstance(SqlSessionFactory sqlSessionFactory) { return new SqlSessionManager(sqlSessionFactory); } ... }
参考:
cnblogs.com/yeweiqiang/p/12940152.html
(5)建造者模式的优缺点
- 优点:
1、建造者独立,易扩展。
2、便于控制细节风险。 - 缺点:
1、产品必须有共同点,范围有限制。
2、如内部变化复杂,会有很多的建造类。