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、如内部变化复杂,会有很多的建造类。




相关文章
|
5天前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
7天前
|
设计模式 算法 数据库连接
PHP中的设计模式:提高代码的可维护性与扩展性
设计模式在PHP开发中至关重要,如单例模式确保类仅有一个实例并提供全局访问点,适用于管理数据库连接或日志记录。工厂模式封装对象创建过程,降低系统耦合度;策略模式定义算法系列并使其可互换,便于实现不同算法间的切换。合理选择设计模式需基于需求分析,考虑系统架构,并通过测试驱动开发验证有效性,确保团队协作一致性和代码持续优化。设计模式能显著提升代码质量,解决开发中的设计难题。
24 8
|
5天前
|
Java
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
17 5
|
7天前
|
Java API 开发者
探索Java中的Lambda表达式:简洁与强大的代码实践
本文深入探讨Java中Lambda表达式的定义、用法及优势,通过实例展示其如何简化代码、提升可读性,并强调在使用中需注意的兼容性和效率问题。Lambda作为Java 8的亮点功能,不仅优化了集合操作,还促进了函数式编程范式的应用,为开发者提供了更灵活的编码方式。
|
3天前
|
Java 开发者
探索Java中的Lambda表达式:简化你的代码之旅##
【8月更文挑战第62天】 Java 8的发布为开发者带来了诸多新特性,其中最引人注目的无疑是Lambda表达式。这一特性不仅让代码变得更加简洁,还极大地提升了开发的效率。本文将通过实际示例,展示如何利用Lambda表达式来优化我们的代码结构,同时探讨其背后的工作原理和性能考量。 ##
|
4天前
|
设计模式 算法 PHP
PHP中的设计模式:提升代码的灵活性与可维护性
在本文中,我们将深入探讨PHP编程语言中的一种重要概念——设计模式。设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它代表了最佳的实践,被有经验的面向对象的软件开发人员所采用。本文将通过具体的实例,展示如何在PHP项目中应用设计模式,以提高代码的灵活性和可维护性。无论你是PHP初学者还是经验丰富的开发者,都能从中获得有价值的见解。
|
6天前
|
设计模式 算法 PHP
PHP中的设计模式:策略模式的深入探索与实践在软件开发的广袤天地中,PHP以其独特的魅力和强大的功能,成为无数开发者手中的得力工具。而在这条充满挑战与机遇的征途上,设计模式犹如一盏明灯,指引着我们穿越代码的迷雾,编写出更加高效、灵活且易于维护的程序。今天,就让我们聚焦于设计模式中的璀璨明珠——策略模式,深入探讨其在PHP中的实现方法及其实际应用价值。
策略模式,这一设计模式的核心在于它为软件设计带来了一种全新的视角和方法。它允许我们在运行时根据不同情况选择最适合的解决方案,从而极大地提高了程序的灵活性和可扩展性。在PHP这门广泛应用的编程语言中,策略模式同样大放异彩,为开发者们提供了丰富的创作空间。本文将从策略模式的基本概念入手,逐步深入到PHP中的实现细节,并通过一个具体的实例来展示其在实际项目中的应用效果。我们还将探讨策略模式的优势以及在实际应用中可能遇到的挑战和解决方案,为PHP开发者提供一份宝贵的参考。
|
6天前
|
Java API 开发者
探索Java中的Lambda表达式:简化代码,提升效率
【9月更文挑战第27天】在Java 8中引入的Lambda表达式为编程带来了革命性的变化。通过简洁的语法和强大的功能,它不仅简化了代码编写过程,还显著提升了程序的执行效率。本文将深入探讨Lambda表达式的本质、用法和优势,并结合实例演示其在实际开发中的应用。无论你是Java新手还是资深开发者,都能从中获得启发,优化你的代码设计。
|
5天前
|
设计模式 存储 数据库连接
探索PHP中的设计模式:提高代码的可维护性与扩展性
本文将深入探讨PHP中常用的设计模式,包括单例模式、工厂模式和观察者模式。通过具体的代码示例,展示如何在实际项目中应用这些设计模式,以提高代码的可维护性与扩展性。无论你是PHP初学者还是有一定经验的开发者,都可以通过本文的学习,提升你的编程技巧和项目架构能力。
|
7天前
|
Java Linux Python
Linux环境下 代码java调用python出错
Linux环境下 代码java调用python出错
21 3
下一篇
无影云桌面