设计模式深入学习

持续更新中。更为详细的进阶资料,欢迎访问我的博客❤

单例模式

1. 饿汉式

/**
 * 饿汉式(静态变量)
 */
public class SingletonDemo1 {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2);
    }

}

class Singleton {
    // 1.构造器私有化,外部能new
    private Singleton() {

    }

    // 2.本类内部创建对象实例
    private final static Singleton instance = new Singleton();

    // 3.提供一个公用的静态方法,返回实例对象
    public static Singleton getInstance() {
        return instance;
    }

}


// =============================> 优化

// 2.本类内部创建对象实例
private static Singleton2 instance;

// 静态代码块中实例化对象
static {
    instance = new Singleton2();
}

2. 懒汉式

/**
 * 懒汉式(线程不安全)
 * 1.起到了懒加载效果,但仅限于单线程下
 * 2.如果在多线程下执行,很有可能会产生多个实例
 * 3.实际开发中,不推荐使用
 */
public class SingletonDemo3 {
    public static void main(String[] args) {
        Singleton5 instance1 = Singleton5.getInstance();
        Singleton5 instance2 = Singleton5.getInstance();
        System.out.println(instance1 == instance2);
    }
}

class Singleton3 {
    private static Singleton3 instance;

    private Singleton3() {
    }

    /**
     * 提供一个静态的公共方法,当使用到该方法时,才会去创建instance
     * 可以加synchronized锁上方法,但是效率太低
     */
    public static Singleton3 getInstance() {
        if (instance == null) {
            instance = new Singleton3();
        }
        return instance;
    }
}
/**
 * 懒汉式(线程安全)
 */
class Singleton5 {
    /**
     * 防止指令重排
     * 变量共享可见
     * 非原子性
     */
    private static volatile Singleton5 instance;

    private Singleton5() {
    }

    /**
     * 提供一个静态的公共方法,加入双重检查代码,解决线程安全问题,同时解决懒加载问题
     * 同时保证了效率,推荐使用
     */
    public static Singleton5 getInstance() {
        // 判断是否有了Singleton4对象
        if (instance == null) {
            // 同步锁
            synchronized (Singleton5.class) {
                // 保证单例
                if (instance == null) {
                    instance = new Singleton5();
                }
            }
        }
        return instance;
    }
}

3. 静态内部类

/**
 * 静态内部类,推荐使用
 */
public class SingletonDemo5 {
    public static void main(String[] args) {
        Singleton5 instance1 = Singleton5.getInstance();
        Singleton5 instance2 = Singleton5.getInstance();
        System.out.println("静态内部类,推荐使用:" + (instance1 == instance2));
    }
}

class Singleton5 {
    private static Singleton5 instance;

    private Singleton5() {
    }

    /**
     *  提供一个静态代码类,里面有静态属性
     */
    private static class SingletonInstance {
        private static final Singleton5 INSTANCE = new Singleton5();
    }

    /**
     * 提供一个静态的公共方法,直接返回SingletonInstance.INSTANCE
     */
    public static Singleton5 getInstance() {
        return SingletonInstance.INSTANCE;
    }
}

4. 枚举

  • Effective Java 作者 Josh Bloch 提倡的方式
/**
 * @author Blue
 * @date 2020/11/2
 **/
public class SingletonDemo {

    public static void main(String[] args) {
        Singleton instance = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;
        System.out.println(instance == instance2);

        System.out.println(instance.hashCode());
        System.out.println(instance2.hashCode());

        instance.sayOK();
    }
}

/**
 * 使用枚举,可以实现单例, 推荐
 */
enum Singleton {
    /**
     * 属性
     */
    INSTANCE; 
    
    public void sayOK() {
        System.out.println("ok~");
    }
}

5.单例模式在 JDK 应用的源码分析

  • java.lang.Runtime 就是经典的单例模式(饿汉式)

原型模式

1.基本介绍

原型模式(Prototype 模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象

原型模式是一种 创建型设计模式,允许一个 对象再创建另外一个可定制的对象,无需知道如何创建的细节

工作原理是: 通过将一个原型对象传给那个要发动创建的对象,这个 要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即 原型对象.clone()

2.传统方式解决克隆羊问题

克隆羊问题:
现在有一只羊 tom,姓名为: tom, 年龄为:1,颜色为:白色,请编写程序创建和 tom 羊 属性完全相同的 10只羊。

/**
 * @author Blue
 * @date 2020/11/2
 **/
public class Client {

    public static void main(String[] args) {

        //传统的方法
        Sheep sheep = new Sheep("tom", 1, "白色");

        Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());


        Sheep sheep5 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        //....
    }

}

  • 传统的方式的优缺点

    • 优点是比较好理解,简单易操作。
    • 在创建新的对象时,总是需要 重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低
    • 总是需要重新初始化对象,而不是动态地获得对象运行时的状态, 不够灵活
  • 改进的思路分析:

    • Object 类提供了一个 clone()方法,该方法可以将一个 Java 对象复制一份
    • 实现 clone 的 Java 类必须要实现一个 接口 Cloneable,该接口表示 该类能够复制且具有复制的能力 =>原型模式

3.原型模式解决克隆羊问题的应用实例

/**
 * @author Blue
 * @date 2020/11/3
 * 克隆羊类 实现 Cloneable接口
 **/
public class Sheep implements Cloneable {
    private String name;
    private int age;
    private String color;
    private String address = "蒙古羊";
    public Sheep friend; //是对象,  克隆是会如何处理,  默认是浅拷贝

    public Sheep(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                ", address='" + address + '\'' +
                ", friend=" + friend +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    //克隆该实例,使用默认的 clone 方法来完成
    @Override
    protected Object clone() {
        Sheep sheep = null;
        try {
            sheep = (Sheep) super.clone();
        } catch (Exception e) {
            e.getStackTrace();
        }
        return sheep;
    }
}
Sheep sheep = new Sheep("tom", 1, "白色");
sheep.friend = new Sheep("jack", 2, "黑色");
Sheep sheep2 = (Sheep) sheep.clone();

// sheep2 = Sheep{name='tom', age=1, color='白色', address='蒙古羊', friend=Sheep{name='jack', age=2, color='黑色', address='蒙古羊', friend=null}}
System.out.println("sheep2 =" + sheep2 + "sheep2.friend=");

4.原型模式在 Spring 框架中源码分析

Spring 中原型 bean 的创建,就是原型模式的应用(后面代码debug)

5.深入讨论-浅拷贝和深拷贝

  • 浅拷贝的介绍

    • 对于数据类型是 基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该 属性值复制一份给新的对象
    • 对于数据类型是 引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行 引用传递,也就是只是将该 成员变量的引用值(内存地址)复制一份给新的对象。因为 实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
    • 前面我们克隆羊就是浅拷贝
    • 浅拷贝是使用 默认的 clone()方法来实现 sheep = (Sheep) super.clone();
  • 深拷贝基本介绍

    • 复制对象的所有基本数据类型的成员变量值
    • 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝
    • 深拷贝实现方式:1.重写 clone 方法来实现深拷贝 2.通过对象序列化实现深拷贝(推荐)

6.深拷贝应用实例

7.原型模式的注意事项和细节

  • 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
  • 不用重新初始化对象,而是动态地获得对象运行时的状态
  • 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
  • 在实现深克隆的时候可能需要比较复杂的代码
  • 缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了 ocp 原则.

建造者模式

前言(需要建房子):
这一过程为打桩、砌墙、封顶房子有各种各样的,
比如普通房,高楼,别墅,各种房子的过程虽然一样,但是要求不要相同的.
请编写程序,完成需求.

1.传统模式解决盖房需求

  • 思路分析

  • 代码演示

public abstract class AbstractHouse {

    //打地基
    public abstract void buildBasic();
    //砌墙
    public abstract void buildWalls();
    //封顶
    public abstract void roofed();

    public void build() { 
        buildBasic();
        buildWalls(); 
        roofed();
    }

}

// ==========================================================

public class CommonHouse extends AbstractHouse {

    @Override
    public void buildBasic() {
        System.out.println(" 普通房子打地基 ");
    }

    @Override
    public void buildWalls() {
        System.out.println(" 普通房子砌墙 ");
    }

    @Override
    public void roofed() {
        System.out.println(" 普通房子封顶 ");
    }

}

  • 问题分析
    • 优点是比较好理解,简单易操作。

    • 设计的程序结构,过于简单,没有设计缓存层对象,程序的扩展和维护不好. 也就是说,这种设计方案,把产品(即:房子) 和 创建产品的过程(即:建房子流程) 封装在一起,耦合性增强了。

    • 解决方案:将产品和产品建造过程解耦 => 建造者模式.

2.基本介绍

建造者模式(Builder Pattern) 又叫生成器模式,是一种对象构建模式。
它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法
可以构造出不同表现(属性)的对象。

建造者模式 是一步一步创建一个复杂的对象,它允许用户只通过
指定复杂对象的类型和内容就可以构建它们, 用户不需要知道内部的具体构建细节。

3.四个角色

  • Product(产品角色): 一个具体的产品对象。

  • Builder(抽象建造者): 创建一个 Product 对象的各个部件指定的 接口/抽象类。

  • ConcreteBuilder(具体建造者): 实现接口,构建和装配各个部件。

  • Director(指挥者): 构建一个使用 Builder 接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。

4.应用实例

  • 思路分析图解

  • 代码实现
public class Client {

    public static void main(String[] args) {
        //盖普通房子
        CommonHouse commonHouse = new CommonHouse();

        //准备创建房子的指挥者
        HouseDirector houseDirector = new HouseDirector(commonHouse);

        //完成盖房子,返回产品(普通房子)
        House house = houseDirector.constructHouse();

        //盖高楼
        HighBuilding highBuilding = new HighBuilding();
        //重置建造者houseDirector.setHouseBuilder(highBuilding);
        //完成盖房子,返回产品(高楼) houseDirector.constructHouse();
    }
}


=====================================================


public class CommonHouse extends HouseBuilder {

    @Override
    public void buildBasic() {
        System.out.println(" 普通房子打地基 5 米 ");
    }

    @Override
    public void buildWalls() {
        System.out.println(" 普通房子砌墙 10cm ");
    }

    @Override
    public void roofed() {
        System.out.println(" 普通房子屋顶 ");
    }
 
}

==================================================

public class HighBuilding extends HouseBuilder {

    @Override
    public void buildBasic() {
        System.out.println(" 高楼的打地基 100 米 ");
    }

    @Override
    public void buildWalls() {

    }

    @Override
    public void roofed() {
        System.out.println(" 高楼的透明屋顶 ");
    }

}


==================================================


// 产 品 ->Product 
public class House {

    private String baise; 
    private String wall; 
    private String roofed; 
    
    public String getBaise() {
        return baise;
    }
    public void setBaise(String baise) { 
        this.baise = baise;
    }
    public String getWall() { 
        return wall;
    }
    public void setWall(String wall) { 
        this.wall = wall;
    }
    public String getRoofed() { 
        return roofed;
    }
    public void setRoofed(String roofed) { 
        this.roofed = roofed;
    }

}



==================================================

// 抽象的建造者
public abstract class HouseBuilder {

    protected House house = new House();

    //将建造的流程写好, 抽象的方法
    public abstract void buildBasic(); 
    public abstract void buildWalls(); 
    public abstract void roofed();

    //建造房子好, 将产品(房子) 返回
    public House buildHouse() { 
        return house;
    }

}


==================================================


//指挥者,这里去指定制作流程,返回产品
public class HouseDirector {

    HouseBuilder houseBuilder = null;

    //构造器传入 houseBuilder
    public HouseDirector(HouseBuilder houseBuilder) { 
        this.houseBuilder = houseBuilder;
    }

    //通过 setter 传入 houseBuilder
    public void setHouseBuilder(HouseBuilder houseBuilder) { 
        this.houseBuilder =  houseBuilder;
    }

    //如何处理建造房子的流程,交给指挥者 
    public House constructHouse() {
        houseBuilder.buildBasic(); 
        houseBuilder.buildWalls(); 
        houseBuilder.roofed();
        return houseBuilder.buildHouse();
    }

}

适配器模式(Adapter Pattern)

1.基本介绍

  • 将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)

  • 属于 结构型模式

  • 主要分为三类:类适配器模式、对象适配器模式、接口适配器模式

2.工作原理

  • 将一个类的接口转换成另一种接口.让原本接口不兼容的类可以兼容
  • 从用户的角度看不到被适配者,是 解耦的
  • 用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法
类适配器模式

基本介绍:Adapter 类,通过继承被适配者 类,实现目标类接口,完成 被适配者 到 目标 的适配

代码实现

首先有一个已存在的将被适配的类

public class Adaptee {
    public void adapteeRequest() {
        System.out.println("被适配者的方法");
    }
}

定义一个目标类接口

public interface Target {
    void request();
}

如果通过一个适配器类,实现 Target 接口,同时 继承了 Adaptee 类,然后在实现的 request() 方法中调用父类的 adaptee 中 adapteeRequest() 即可实现

public class Adapter extends Adaptee implements Target{
    @Override
    public void request() {
        System.out.println("Target目标方法");
        super.adapteeRequest();
    }
}

测试

public class Test {
    public static void main(String[] args) {
        Target adapterTarget = new Adapter();
        adapterTarget.request();
        // 输出:concreteTarget目标方法
        //       被适配者的方法
    }
}

对象适配器模式

基本介绍:对象适配器与类适配器不同之处在于,类适配器通过继承来完成适配,对象适配器则是通过关联来完成,这里稍微修改一下 Adapter 类即可将转变为对象适配器

修改 Adapter 适配器类 即可

public class Adapter implements Target{

    // 关键:适配者是对象适配器的一个属性
    private Adaptee adaptee = new Adaptee();

    @Override
    public void request() {
        System.out.println("Target目标方法");
        adaptee.adapteeRequest();
    }
}

接口适配器模式

基本介绍:适配器模式(Default Adapter Pattern)或缺省适配器模式,适用于一个接口不想使用其所有的方法的情况

核心思路:当 不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并 为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可 有选择地覆盖父类的某些方法来实现需求

提供接口

public interface InterfaceProvide {
    void run1();
    void run2();
    void run3();
    void run4();
}

实现接口,默认实现空方法

public class AbsAdapter implements InterfaceProvide{
    @Override
    public void run1() {
        // 默认实现
    }

    @Override
    public void run2() {

    }

    @Override
    public void run3() {

    }

    @Override
    public void run4() {

    }
}

测试

public class Test {

    public static void main(String[] args) {
        AbsAdapter absAdapter = new AbsAdapter() {
            //只需要去覆盖我们 需要使用 接口方法
            @Override
            public void run1() {
                System.out.println("使用了 run1 的方法");
            }
        };
        // 输出:使用了 run1 的方法
        absAdapter.run1();
        absAdapter.run2();
    }
}

3. 应用:SpringMvc 中的 HandlerAdapter, 就使用了适配器模式

参考:设计模式 | 适配器模式及典型应用

桥接模式(Bridge 模式)

1.基本介绍

  • 将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变
  • 是一种结构型设计模式
  • 基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。

2.思路分析

3.代码

钢笔类

public abstract class Pen {
    
    protected Color color;

    /**
     * 桥接模式的关键,组合实现和抽象
     */
    public Pen(Color color){
        this.color= color;
    }

    /**
     * 着色方法
     */
    public abstract void tinting();
}

钢笔类扩展-小号钢笔

public class SmallPen extends Pen {

    /**
     * 着色方法:桥接模式的关键,组合实现和抽象
     */
    public SmallPen(Color color) {
        super(color);
    }

    @Override
    public void tinting() {
        System.out.println(super.color.colour() + "小号钢笔");
    }

}

钢笔类扩展-中号钢笔

public class MiddlePen extends Pen {

    /**
     * 着色方法:桥接模式的关键,组合实现和抽象
     */
    public MiddlePen(Color color) {
        super(color);
    }

    @Override
    public void tinting() {
        System.out.println(super.color.colour() + "中号钢笔");
    }
}

颜色接口

/**
 * 实现类接口
 * @author coderblue
 */
public interface Color {
    // 返回对应的颜色
    String colour();
}

实现颜色接口 - 绿色

public class Green implements Color {

    @Override
    public String colour() {
        return "绿色的";
    }
}

实现颜色接口 - 红色

public class Red implements Color {

    @Override
    public String colour() {
        return "红色的";
    }
}

测试

public class Test {
    public static void main(String[] args) {
        // 绿色的中号钢笔
        Pen middlePen = new MiddlePen(new Green());
        middlePen.tinting();

        // 红色的小号钢笔
        Pen smallPen = new SmallPen(new Red());
        smallPen.tinting();
    }
}

装饰者设计模式

1.基本介绍

动态地给一个对象增加一些额外的职责,增加对象功能来说,装饰模式比生成子类实现更为灵活。装饰模式是一种对象结构型模式。
2.角色

Component(抽象构件)它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。
ConcreteComponent(具体构件)它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。
Decorator(抽象装饰类)它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。
ConcreteDecorator(具体装饰类)它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。
由于具体构件类和装饰类都实现了相同的抽象构件接口,因此装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。
装饰模式的核心在于抽象装饰类的设计。

3.思路分析

4.代码

抽象构件

/**
 * 煎饼抽象类
 * @author coderblue
 */
public abstract class ABattercake {
    /**
     * 描述
     */
    protected abstract String getDescrible();

    /**
     * 价格
     */
    protected abstract int cost();
}

具体构件

/**
 * 煎饼类,继承了煎饼抽象类,一个煎饼 8 块钱(具体构件)
 */
public class Battercake extends ABattercake {

    @Override
    protected String getDescrible() {
        return "煎饼";
    }

    @Override
    protected int cost() {
        return 8;
    }
}

抽象装饰类

/**
 * 抽象装饰类
 */
public abstract class AbstractDecorator extends ABattercake {
    private ABattercake aBattercake;

    public AbstractDecorator(ABattercake aBattercake) {
        this.aBattercake = aBattercake;
    }

    protected abstract void doSomething();

    @Override
    protected String getDescrible() {
        return this.aBattercake.getDescrible();
    }

    @Override
    protected int cost() {
        return this.aBattercake.cost();
    }
}

具体装饰类

/**
 * 鸡蛋装饰器,继承了抽象装饰类
 */
public class EggDecorator extends AbstractDecorator {
    public EggDecorator(ABattercake aBattercake) {
        super(aBattercake);
    }

    @Override
    protected void doSomething() {

    }

    @Override
    protected String getDescrible() {
        return super.getDescrible() + " 加一个鸡蛋";
    }

    @Override
    protected int cost() {
        return super.cost() + 1;
    }

}

/**
 * 香肠装饰器
 */
public class SausageDecorator extends AbstractDecorator {
    public SausageDecorator(ABattercake aBattercake) {
        super(aBattercake);
    }

    @Override
    protected void doSomething() {

    }

    @Override
    protected String getDescrible() {
        return super.getDescrible() + " 加一根香肠";
    }

    @Override
    protected int cost() {
        return super.cost() + 2;
    }
}

测试

public class Test {
    public static void main(String[] args) {
        ABattercake aBattercake = new Battercake();
        // 1.购买一个煎饼:煎饼, 销售价格: 8
        System.out.println(aBattercake.getDescrible() + ", 销售价格: " + aBattercake.cost());

        // 2.购买一个加鸡蛋的煎饼:煎饼 加一个鸡蛋, 销售价格: 9
        aBattercake = new EggDecorator(aBattercake);
        System.out.println(aBattercake.getDescrible() + ", 销售价格: " + aBattercake.cost());

        // 3.购买一个加两个鸡蛋和一根香肠的煎饼: 煎饼 加一个鸡蛋 加一个鸡蛋 加一根香肠, 销售价格: 12
        aBattercake = new EggDecorator(aBattercake);
        aBattercake = new EggDecorator(aBattercake);
        aBattercake = new SausageDecorator(aBattercake);
        System.out.println(aBattercake.getDescrible() + ", 销售价格: " + aBattercake.cost());
    }
}

参考:设计模式 | 装饰者模式及典型应用

工厂模式

简单工厂模式(Simple Factory Pattern)

1.基本介绍

定义一个工厂类,它可以 根据参数的不同返回不同类的实例,被创建的实例通常都 具有共同的父类

2.角色

  • Factory(工厂角色):工厂角色即 工厂类,它是简单工厂模式的核心,负责实现创建所有产品实例的内部逻辑;工厂类可以 被外界直接调用创建所需的产品对象;在工厂类中提供了静态的工厂方法factoryMethod(),它的返回类型为抽象产品类型Product

  • Product(抽象产品角色):它是 工厂类所创建的所有对象的父类,封装了各种产品对象的公有方法,它的引入将提高系统的灵活性,使得在工厂类中只需定义一个通用的工厂方法,因为所有创建的具体产品对象都是其子类对象。

  • ConcreteProduct(具体产品角色):它是简单工厂模式的 创建目标,所有被创建的对象都充当这个角色的某个具体类的实例。每一个 具体产品角色都继承了抽象产品角色,需要实现在抽象产品中声明的抽象方法

3.思路分析

4.代码

抽象产品类 Video,定义了抽象方法 produce()

public abstract class Video {
    public abstract void produce();
}

具体产品类 JavaVideo 和 PythonVideo,都继承了抽象产品类 Video

public class JavaVideo extends Video {
    @Override
    public void produce() {
        System.out.println("录制Java课程视频");
    }
}

public class PythonVideo extends Video {
    @Override
    public void produce() {
        System.out.println("录制Python课程视频");
    }
}

工厂类实现的两种方法:使用 if-else判断 和使用 反射 来创建对象

public class VideoFactory {
    /**
     * 使用if else 判断类型,type 为 Java 则返回 JavaVideo, type为Python则返回 PythonVideo
     */
    public Video getVideo(String type) {
        // equalsIgnoreCase()作用是用来比较字母的长度和字符是否相同,且不区分大小写。
        if ("Java".equalsIgnoreCase(type)) {
            return new JavaVideo();
        } else if ("Python".equalsIgnoreCase(type)) {
            return new PythonVideo();
        }
        return null;
    }

    /**
     * 使用反射来创建对象
     */
    public Video getVideo(Class c) {
        Video video = null;
        try {
            // Java9后创建对象中间增加了 getDeclaredConstructor() 调用链
            video = (Video) Class.forName(c.getName()).getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return video;
    }
}

测试

public class Test {
    public static void main(String[] args) {
        VideoFactory videoFactory = new VideoFactory();
        Video video1 = videoFactory.getVideo("python");
        if (video1 == null) {
            return;
        }
        // 录制Python课程视频
        video1.produce();

        Video video2 = videoFactory.getVideo(JavaVideo.class);
        if (video2 == null) {
            return;
        }
        // 录制Java课程视频
        video2.produce();
    }
}

5.适用场景

  • 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。

  • 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。

参考:设计模式 | 简单工厂模式及典型应用

工厂方法模式(Factory Method Pattern)

1.基本介绍

  • 定义一个 用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个 类的实例化延迟到其子类
  • 简称为 工厂模式(Factory Pattern),又可称作 虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic Factory Pattern)
  • 工厂方法模式是一种 类创建型模式

2.角色

  • Product(抽象产品):它是定义产品的接口,是工厂方法模式所创建对象的超类型,也就是产品对象的公共父类

  • ConcreteProduct(具体产品):它实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,具体工厂和具体产品之间一一对应。

  • Factory(抽象工厂):在抽象工厂类中,声明了工厂方法(Factory Method),用于返回一个产品。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口。

  • ConcreteFactory(具体工厂):它是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品类的实例。

与简单工厂模式相比,工厂方法模式最重要的区别是引入了抽象工厂角色,抽象工厂可以是接口,也可以是抽象类或者具体类

3.代码思路

4.代码

抽象工厂

public abstract class VideoFactory {
    /**
     * 获得视频
     * @return
     */
    public abstract Video getVideo();
}

具体工厂

public class JavaVideoFactory extends VideoFactory {
    @Override
    public Video getVideo() {
        return new JavaVideo();
    }
}

public class PythonVideoFactory extends VideoFactory {
    @Override
    public Video getVideo() {
        return new PythonVideo();
    }
}

抽象产品

public abstract class Video {
    /**
     * 视频录制
     */
    public abstract void produce();
}

具体产品

public class JavaVideo extends Video {
    @Override
    public void produce() {
        System.out.println("录制Java课程视频");
    }
}

public class PythonVideo extends Video {
    @Override
    public void produce() {
        System.out.println("录制Python课程视频");
    }
}

测试

public class Test {
    public static void main(String[] args) {
        VideoFactory pythonVideoFactory = new PythonVideoFactory();
        VideoFactory javaVideoFactory = new JavaVideoFactory();

        // 录制Python课程视频
        Video pythonVideo = pythonVideoFactory.getVideo();
        pythonVideo.produce();

        // 录制Java课程视频
        Video javaVideo = javaVideoFactory.getVideo();
        javaVideo.produce();
    }
}

当需要增加一个产品 KotlinVideo 时,只需要增加 KotlinVideo 具体产品类和 KotlinVideoFactory 具体工厂类即可,不需要修改原有的产品类和工厂类

public class KotlinVideo extends Video{
    @Override
    public void produce() {
        System.out.println("录制FE课程视频");
    }
}

public class KotlinVideoFactory extends VideoFactory{
    @Override
    public Video getVideo() {
        return new KotlinVideo();
    }
}

还可以通过反射机制和配置文件配合,连客户端代码都不需要修改

// XXVideoFactory在的路径
String factoryName = "cn.coderblue.JavaVideoFactory";
// 通过反射机制获取工厂类
Class c = Class.forName(factoryName);
VideoFactory factory = (VideoFactory)c.getDeclaredConstructor().newInstance();
// 生产产品
Video video = factory.getVideo();
video.produce();

参考:设计模式 | 工厂方法模式及典型应用

抽象工厂模式

1.基本介绍

  • 定义了一个 interface 用于创建相关或有依赖关系的对象簇,而无需指明具体的类
  • 可以将简单工厂模式和工厂方法模式进行整合
  • 从设计层面看,抽象工厂模式就是 对简单工厂模式的改进(或者称为进一步的抽象)。
  • 将工厂抽象成两层,AbsFactory(抽象工厂)具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。

2.角色

  • AbstractFactory(抽象工厂):它声明了一组用于创建一族产品的方法,每一个方法对应一种产品。

  • ConcreteFactory(具体工厂):它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。

  • AbstractProduct(抽象产品):它为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法

  • ConcreteProduct(具体产品):它定义具体工厂生产的具体产品对象,实现抽象产品接口中声明的业务方法。

3.代码思路

4.代码

定义我们的抽象产品 Article 和 Video,他们是产品族的抽象类,有一个 Article 就有一个 Video

public abstract class Article {
    public abstract void produce();
}

public abstract class Video {
    public abstract void produce();
}

具体产品 JavaArticle、PythonArticle、PythonVideo、JavaVideo

public class JavaArticle extends Article {
    @Override
    public void produce() {
        System.out.println("编写Java课程笔记记");
    }
}

public class PythonArticle extends Article {
    @Override
    public void produce() {
        System.out.println("编写Python课程笔记");
    }
}

public class JavaVideo extends Video {
    @Override
    public void produce() {
        System.out.println("录制Java课程视频");
    }
}

public class PythonVideo extends Video {
    @Override
    public void produce() {
        System.out.println("录制Python课程视频");
    }
}

定义我们的抽象工厂 CourseFactory,与工厂方法模式不同,工厂方法模式中一个工厂只生产一个产品,而 抽象工厂模式中一个工厂生产一族产品,有 多个工厂方法

public class JavaCourseFactory implements CourseFactory {
    @Override
    public Video getVideo() {
        return new JavaVideo();
    }

    @Override
    public Article getArticle() {
        return new JavaArticle();
    }
}

public class PythonCourseFactory implements CourseFactory {
    @Override
    public Video getVideo() {
        return new PythonVideo();
    }

    @Override
    public Article getArticle() {
        return new PythonArticle();
    }
}

测试:只需要指定具体工厂,就可以获取该工厂生产的一族产品

public class Test {
    public static void main(String[] args) {
        CourseFactory courseFactory = new JavaCourseFactory();
        Video video = courseFactory.getVideo();
        Article article = courseFactory.getArticle();
        // 录制Java课程视频
        video.produce();
        // 编写Java课程笔记
        article.produce();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值