Java代码设计模式讲解二十三种设计模式(一)

简介: Java代码设计模式讲解二十三种设计模式

所有的源码请参考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 类,带有 ItemArrayList 和一个通过结合 Item 来创建不同类型的 Meal 对象的 MealBuilderBuilderPatternDemo,我们的演示类使用 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、如内部变化复杂,会有很多的建造类。




相关文章
|
2天前
|
Java
在 Java 中捕获和处理自定义异常的代码示例
本文提供了一个 Java 代码示例,展示了如何捕获和处理自定义异常。通过创建自定义异常类并使用 try-catch 语句,可以更灵活地处理程序中的错误情况。
|
13天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
16天前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###
|
16天前
|
XML 安全 Java
Java反射机制:解锁代码的无限可能
Java 反射(Reflection)是Java 的特征之一,它允许程序在运行时动态地访问和操作类的信息,包括类的属性、方法和构造函数。 反射机制能够使程序具备更大的灵活性和扩展性
26 5
Java反射机制:解锁代码的无限可能
|
9天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
24 1
|
13天前
|
jenkins Java 测试技术
如何使用 Jenkins 自动发布 Java 代码,通过一个电商公司后端服务的实际案例详细说明
本文介绍了如何使用 Jenkins 自动发布 Java 代码,通过一个电商公司后端服务的实际案例,详细说明了从 Jenkins 安装配置到自动构建、测试和部署的全流程。文中还提供了一个 Jenkinsfile 示例,并分享了实践经验,强调了版本控制、自动化测试等关键点的重要性。
42 3
|
18天前
|
存储 安全 Java
系统安全架构的深度解析与实践:Java代码实现
【11月更文挑战第1天】系统安全架构是保护信息系统免受各种威胁和攻击的关键。作为系统架构师,设计一套完善的系统安全架构不仅需要对各种安全威胁有深入理解,还需要熟练掌握各种安全技术和工具。
50 10
|
14天前
|
分布式计算 Java MaxCompute
ODPS MR节点跑graph连通分量计算代码报错java heap space如何解决
任务启动命令:jar -resources odps-graph-connect-family-2.0-SNAPSHOT.jar -classpath ./odps-graph-connect-family-2.0-SNAPSHOT.jar ConnectFamily 若是设置参数该如何设置
|
12天前
|
Java
Java代码解释++i和i++的五个主要区别
本文介绍了前缀递增(++i)和后缀递增(i++)的区别。两者在独立语句中无差异,但在赋值表达式中,i++ 返回原值,++i 返回新值;在复杂表达式中计算顺序不同;在循环中虽结果相同但使用方式有别。最后通过 `Counter` 类模拟了两者的内部实现原理。
Java代码解释++i和i++的五个主要区别
|
20天前
|
搜索推荐 Java 数据库连接
Java|在 IDEA 里自动生成 MyBatis 模板代码
基于 MyBatis 开发的项目,新增数据库表以后,总是需要编写对应的 Entity、Mapper 和 Service 等等 Class 的代码,这些都是重复的工作,我们可以想一些办法来自动生成这些代码。
28 6

热门文章

最新文章

  • 1
    C++一分钟之-设计模式:工厂模式与抽象工厂
    42
  • 2
    《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
    46
  • 3
    C++一分钟之-C++中的设计模式:单例模式
    53
  • 4
    《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
    37
  • 5
    《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
    61
  • 6
    Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
    56
  • 7
    Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
    40
  • 8
    Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
    49
  • 9
    Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
    105
  • 10
    Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
    75