呕心整理Java中的12种常用设计模式以及应用场景

目录

单例模式

饿汉模式

懒汉模式

线程锁模式

双重判断模式

静态内部类模式

策略模式

工厂模式

简单工厂

抽象工厂

门面模式

装饰器模式

责任链模式

享元模式

观察者模式

代理模式

静态代理模式

动态代理模式

适配器模式

原型模式

模板方法模式


单例模式

        单例模式是java中老生常谈的设计模式,在工作中相信大家也没少接触,就小编个人而言,单例模式的主要应用场景如下:

        适用于项目中频繁获取对象的场景,例如:获取缓存对象、获取一些工具类对象等等,由于这些对象使用频率较高,所以在获取对象时,我们使用单例模式指定获取一个对象即可。

下面小编带大家再次温习一下单例模式的写法,这里将介绍单例模式的五种写法,

饿汉模式

 代码结构如下:

  1. 私有的静态的 最终的 对象 直接new
  2. 私有的 无参构造方法
  3. 共有的 静态的 实例方法

        饿汉模式是单例模式中常用的写法之一,主要的特点是在定义对象的时候就直接new一个对象,详细代码如下:

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 单例模式 饿汉式
 *  上来就new对象
 **/
public class SignletonHungry {

    //1. 私有的静态的最终的对象
    private static final SignletonHungry singl=new SignletonHungry();

    //2. 私有的无参构造函数
    private SignletonHungry(){

    }

    //3. 公共的静态的实例方法
    public static SignletonHungry getInstance(){
        return singl;
    }

    //测试方法
    public static void main(String[] args) {
        //利用for循环 模拟多线程环境调用
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                //看每次获取对象的hashcode是否一致 判断是否获取了同一个对象
                System.out.println("获取的hashCode是: "+SignletonHungry.getInstance().hashCode());
            }).start();
        }
    }
}

        在类的下方我们定义了一个测试方法,用来模拟验证在多线程并发访问时,每次通过单例类获取的对象的hashcode方法是否一致,如果一致则代表线程安全,执行结果如下:

呕心整理Java中的12种常用设计模式以及应用场景_第1张图片

可以看到执行结果是一致的,这种饿汉写法一般在实际项目中应用的也是最多的,

优点:这种写法比较简单,就是在类装载的时候就完成实例化,避免了线程同步问题。

缺点:但是因为在指定对象时就进行初始化,在类比较大的时候,也会造成一定的资源消耗。

懒汉模式

代码结构如下:

  1. 私有的静态的对象 为空 不new
  2. 私有的无参构造方法
  3. 共有的静态的实例方法

        为了避免上述所说的饿汉式的缺点,延伸出了懒汉式的单例写法,在定义对象时,并不直接进行初始化,在实际的实例化方法里面,才进行初始化操作,这样就节省了一定的资源,具体实现代码如下:

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 *
 * 单例模式 懒汉式
 * 调用实例方法时才new对象
 * 节省空间 缺点是线程不安全
 **/
public class SignletonFull {

    //1. 私有的静态的对象 先不new 默认为null值
    private static SignletonFull signletonFull;

    //2. 私有的无参构造器
    private SignletonFull(){}

    //3. 公共的静态的方法
    public static SignletonFull getInstance() throws InterruptedException {
        if(signletonFull==null){
            Thread.sleep(1000);
            signletonFull=new SignletonFull();
        }
        return signletonFull;
    }

    //测试方法
    public static void main(String[] args) {
        //利用for循环 模拟多线程环境调用
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                //看每次获取对象的hashcode是否一致 判断是否获取了同一个对象
                try {
                    System.out.println("获取的hashCode是: "+SignletonFull.getInstance().hashCode());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

        同样,在这个类的下方我们也定义了一个测试方法,用来模拟验证在多线程并发访问时,每次通过单例类获取的对象的hashcode方法是否一致,如果一致则代表线程安全,执行结果如下:

呕心整理Java中的12种常用设计模式以及应用场景_第2张图片

        我们可以看到,懒汉模式在多线程并发获取单例类时,存在现场安全的问题,那么既然存在线程安全问题,我们怎么去改善这个问题呢?请看线程锁模式。

线程锁模式

 代码结构如下:

  1. 私有的静态的对象 为空 不new
  2. 私有的无参构造方法
  3. 共有的静态的实例方法,在判断对象是否为空时加上synchronize修饰

        通过线程锁的写法可以解决懒汉模式下存在的线程安全问题,具体实现代码如下:

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 *
 * 单例模式之 加锁
 * 线程安全 缺点是效率低 受synchronized锁升级的影响
 **/
public class SignletonThread {
    //1. 私有的静态的对象
    private static SignletonThread signletonThread;

    //2. 私有的构造方法
    private SignletonThread(){}

    //3. 公共的静态的实例方法 在if里面加上锁synchronized
    public static SignletonThread getInstance(){
        if (signletonThread==null){
            synchronized (SignletonThread.class){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                signletonThread=new SignletonThread();
            }
        }
        return signletonThread;
    }

    //测试方法
    public static void main(String[] args) {
        //利用for循环 模拟多线程环境调用
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                //看每次获取对象的hashcode是否一致 判断是否获取了同一个对象
                System.out.println("获取的hashCode是: "+SignletonThread.getInstance().hashCode());
            }).start();
        }
    }
}

        同样,在这个类的下方我们也定义了一个测试方法,用来模拟验证在多线程并发访问时,每次通过单例类获取的对象的hashcode方法是否一致,如果一致则代表线程安全,执行结果如下: 

呕心整理Java中的12种常用设计模式以及应用场景_第3张图片

         我们可以看到,执行结果并不如人意,为什么呢,这是因为在执行到synchronized代码快的时候,有线程已经获取到了对象,从而导致获取的对象不一致的情况,那么如何解决这个问呢?

双重判断模式

代码结构如下:

  1. 私有的静态的对象 为空 不new
  2. 私有的无参构造方法
  3. 共有的静态的实例方法,在判断对象是否为空时加上synchronize修饰
  4. 在判断里面再次判断是否为空
package designmodel.signelton;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 *
 * 单例写法
 * 双重判断式
 **/
public class SignletonThreadTwo {

    //1. 私有的静态的对象
    private static SignletonThreadTwo signletonThreadTwo;

    //2. 私有的构造方法
    private SignletonThreadTwo(){}

    //3. 公共的静态的实例方法 在if里面加上锁synchronized 在锁块中继续判断是否为空
    public static SignletonThreadTwo getInstance(){
        if (signletonThreadTwo==null){
            synchronized (SignletonThreadTwo.class){
                if(signletonThreadTwo==null){
                    signletonThreadTwo=new SignletonThreadTwo();
                }
            }
        }
        return signletonThreadTwo;
    }

    //测试方法
    public static void main(String[] args) {
        //利用for循环 模拟多线程环境调用
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                //看每次获取对象的hashcode是否一致 判断是否获取了同一个对象
                System.out.println("获取的hashCode是: "+SignletonThreadTwo.getInstance().hashCode());
            }).start();
        }
    }
}

        同样,在这个类的下方我们也定义了一个测试方法,用来模拟验证在多线程并发访问时,每次通过单例类获取的对象的hashcode方法是否一致,如果一致则代表线程安全,执行结果如下:  

呕心整理Java中的12种常用设计模式以及应用场景_第4张图片

        我们看执行结果,虽然执行结果是我们想要的,但是由于引入了synchronized代码块,所以也引入了轻量级锁、重量级锁的概念,虽然保障了线程安全,但是却失去了性能加成并且容易导致死锁,所以,有没有什么办法,既能线程安全,又能保障效率呢? 

静态内部类模式

代码结构如下:

  1. 私有的无参构造器
  2. 私有的静态的内部类
  3. 在内部类中定义私有的最终的静态的对象,new一个
  4. 定义公共的实例方法,返回内部类.对象
package designmodel.signelton;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 单例模式
 * 通过静态内部类实现懒加载与线程安全
 * 利用JVN特性实现 JVM在加载类和内部类的时候 只会在运行的时候加载一次 从而保证线程安全和懒加载
 **/
public class SignletonStaticClass {

    //1. 私有的无参构造器
    private SignletonStaticClass(){}

    //2. 私有的静态的内部类
    private static class SignletonStatic{
        //3. 在私有的内部类中定义私有的 最终的 静态的对象
        private final static SignletonStaticClass signletonStaticClass=new SignletonStaticClass();
    }

    //4. 公共的静态的实例方法
    public static SignletonStaticClass getInstance(){
        return SignletonStatic.signletonStaticClass;
    }

    //测试方法
    public static void main(String[] args) {
        //利用for循环 模拟多线程环境调用
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                //看每次获取对象的hashcode是否一致 判断是否获取了同一个对象
                System.out.println("获取的hashCode是: "+SignletonStaticClass.getInstance().hashCode());
            }).start();
        }
    }

}

        在执行验证代码时,我们可以看到,通过此写法保障了线程安全和效率,这个写法的原理类似于饿汉式,利用JVN特性实现 JVM在加载类和内部类的时候 只会在运行的时候加载一次 从而保证线程安全和懒加载。

呕心整理Java中的12种常用设计模式以及应用场景_第5张图片

策略模式

代码结构如下:

  1. 定义策略接口,定义通用方法。
  2. 定义N个实现类,实现接口,重新方法。
  3. 定义Context上下文类,利用多态进行封装。
  4. 使用时通过Context上下文类进行调用,在构造函数中传入实现类的对象。

     策略模式:策略模式是一种行为型模式,它将对象和行为分开,将行为定义为 一个行为接口 和 具体行为的实现。策略模式最大的特点是行为的变化,行为之间可以相互替换。每个if判断都可以理解为就是一个策略,可以使得算法可独立于使用它的用户而变化。

    使用场景:

                       1. 假设现在某超市有三个等级的会员,普通会员,VIP1,VIP2。

                       2. 在结账的时候,三个登记的会员购买了同一种商品,普通会员不打折,VIP1打9折,VIP2打8折

定义策略接口:

package designmodel.celve;

/**
 * @Author luocong
 * @Description //TODO
 * @Date 12:20 2022/11/8
 * @Param 
 * @return
 * 定义策略接口
 * 案例场景:
 *  有三种会员 购买相同数量和单价的产品时 需要打不同的折扣
 **/
public interface StrategyInt {

    //price价格 n数量
    public double getPrice(double price,int n);
}

定义普通会员,实现策略接口:

package designmodel.celve;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 实现类1 实现接口中定义的计算价格方法
 * 普通会员类 不打折
 **/
public class NormalPerson implements StrategyInt {

    //普通会员不打折
    @Override
    public double getPrice(double price, int n)
    {
        System.out.println("普通会员不打折.....");
        return (price*n);
    }
}

 定义vip1会员,实现策略接口:

package designmodel.celve;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 *实现类2 实现接口中定义的计算价格方法
 *VIP1会员 打9折
 **/
public class Vip1Person implements StrategyInt{

    //VIP1客户 打9折
    @Override
    public double getPrice(double price, int n) {
        System.out.println("VIP1打9折.....");
        return (price*n)*0.9;
    }
}

 定义vip2会员,实现策略接口: 

package designmodel.celve;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 实现类2 实现接口中定义的计算价格方法
 * VIP2会员类 打8折
 **/
public class Vip2Person implements StrategyInt {

    @Override
    public double getPrice(double price, int n) {
        System.out.println("VIP2打8折.....");
        return (price*n)*0.8;
    }
}

定义context上下文类,利用多态进行封装: 

package designmodel.celve;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 上下文类 对实现类和接口进行封装
 **/
public class PersonContext {
    //1. 定义私有对象
    private StrategyInt strategyInt;

    //2. 定义有参构造方法
    public PersonContext(StrategyInt strategyInt) {
        this.strategyInt = strategyInt;
    }

    //3. 定义计算价格的方法
    public double getPrice(double price,int n){
        return strategyInt.getPrice( price, n);
    }


}

定义测试类,调用方法:

package designmodel.celve;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 测试类  演示策略模式的使用场景
 **/
public class StrategyTest {

    public static void main(String[] args) {
        //定义三个类型的对象
        NormalPerson normalPerson = new NormalPerson();
        Vip1Person vip1Person = new Vip1Person();
        Vip2Person vip2Person = new Vip2Person();
        //new context类对象 将三个类型的对象传入
        PersonContext npersonContext = new PersonContext(normalPerson);
        PersonContext v1personContext = new PersonContext(vip1Person);
        PersonContext v2personContext = new PersonContext(vip2Person);
        //利用多态 通过调用context类对象的计算价格方法 实际上调用的子类的计算价格方法 得到最终价格
        System.out.println("普通会员: "+npersonContext.getPrice(300,20));
        System.out.println("VIP1: "+v1personContext.getPrice(300,20));
        System.out.println("VIP2: "+v2personContext.getPrice(300,20));
    }
}

执行结果如下:

 呕心整理Java中的12种常用设计模式以及应用场景_第6张图片

工厂模式

        相信大家也没少使用工厂模式,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式,在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

       在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 几次对象就不用了, 这种情况,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

简单工厂

        简单工厂是工厂模式的简单实现的写法,这种写法比较简便,同时也带来了耦合的问题,我们来定义飞机和汽车以及生产他们的工厂类,主要实现代码如下:

飞机类:

package designmodel.factory.samplefactory;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 飞机类
 **/
public class Plane {
    public void go(){
        System.out.println("飞机嗖嗖嗖....");
    }
}

汽车类:

package designmodel.factory.samplefactory;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 汽车类
 **/
public class Car {
    public void go(){
        System.out.println("汽车滴滴滴....");
    }
}

生产交通工具的工厂类 :

package designmodel.factory.samplefactory;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 工厂类
 **/
public class ProductFactory {

    public Car getCar(){
        return new Car();
    }

    public Plane getPlane(){
        return new Plane();
    }
}

测试类: 

package designmodel.factory.samplefactory;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 测试类
 **/
public class TestClass {

    public static void main(String[] args) {
        Car car = new ProductFactory().getCar();
        Plane plane = new ProductFactory().getPlane();
        car.go();
        plane.go();
    }
}

执行结果如下:

 呕心整理Java中的12种常用设计模式以及应用场景_第7张图片

        这种简单工厂的写法 比较简单方便,但缺点也是显而易见的,就是定义的类耦合度太高,后续如果新增了对象,需要多次修改工厂类,怎么解决这个缺点呢?请看抽象工厂模式。

抽象工厂

应⽤场景:

  • 解耦:分离职责,把复杂对象的创建和使的过程分开。
  • 代码 降低维护成本:如果对象创建复杂且多处需到,如果每处都进编写,则很多重复代码,如果业务逻辑发了改 变,需四处修改;使⽤⼯⼚模式统创建,则只要修改⼯⼚类即可, 降低成本。

代码结构如下:

      以新能源汽车为例,五菱和特斯拉。

  1. 定义汽车接口,汽车接口中定义了一些方法,比如启动、运行、关闭。
  2. 定义特斯拉和五菱的汽车类,重写汽车接口的这些方法。
  3. 定义生产汽车的工厂接口,接口中有生产汽车的方法,返回值类型为汽车接口。
  4. 分别定义特斯拉和五菱的工厂类,实现生产汽车的工厂接口,重写生产汽车的方法。
  5. 测试类中直接new五菱和特斯拉的工厂类,生产出相关的产品,调用启动、运行、关闭方法。

定义汽车接口:

package designmodel.factory.abstractfactory;
/**
 * @Author luocong
 * @Description //TODO
 * @Date 19:35 2022/11/8
 * @Param 
 * @return
 * 定义汽车的接口 汽车可以启动、跑、关闭
 **/
public interface CarProduct {
    //启动
    void start();
    //跑
    void run();
    //关闭
    void shutDown();
}

 定义特斯拉汽车类,实现汽车接口:

package designmodel.factory.abstractfactory;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 定义具体的实现类
 * 特斯拉汽车
 **/
public class TeslaCar implements CarProduct {
    @Override
    public void start() {
        System.out.println("特斯拉启动了");
    }

    @Override
    public void run() {
        System.out.println("特斯拉跑了");
    }

    @Override
    public void shutDown() {
        System.out.println("特斯拉关闭了");
    }
}

定义五菱汽车类,实现汽车接口:

package designmodel.factory.abstractfactory;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 定义具体的实现类
 * 五菱汽车
 **/
public class WulingCar implements CarProduct {
    @Override
    public void start() {
        System.out.println("五菱启动了");
    }

    @Override
    public void run() {
        System.out.println("五菱开始跑了");
    }

    @Override
    public void shutDown() {
        System.out.println("五菱关闭了");
    }
}

定义生产汽车的工厂类: 

package designmodel.factory.abstractfactory;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 定义生产汽车的工厂类
 *
 **/
public interface CarproductFactory {

    CarProduct productCar();
}

定义生产五菱汽车的工厂类: 

package designmodel.factory.abstractfactory;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 定义生产五菱的汽车工厂
 * 生成五菱汽车的对象
 **/
public class WuLingFactory implements CarproductFactory {
    @Override
    public CarProduct productCar() {
        return new WulingCar();
    }
}

定义生产特斯拉的工厂类:

package designmodel.factory.abstractfactory;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 定义特斯拉汽车的工厂
 * 生产特斯拉汽车
 **/
public class TeslaFactory implements CarproductFactory {
    @Override
    public CarProduct productCar() {
        return new TeslaCar();
    }
}

测试类: 

package designmodel.factory.abstractfactory;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 **/
public class TestFactory {

    public static void main(String[] args) {
        //生产五菱汽车
        WuLingFactory wuLingFactory = new WuLingFactory();
        CarProduct carProduct = wuLingFactory.productCar();
        carProduct.start();
        carProduct.run();
        carProduct.shutDown();
        System.out.println("*******************************");
        //生产特斯拉汽车
        TeslaFactory teslaFactory = new TeslaFactory();
        CarProduct carProduct1 = teslaFactory.productCar();
        carProduct1.start();
        carProduct1.run();
        carProduct1.shutDown();
    }
}

   执行结果如下:

呕心整理Java中的12种常用设计模式以及应用场景_第8张图片

        通过抽象工厂我们可以减少代码之间的耦合度,比如后续再新增小鹏汽车类的话,直接新增小鹏汽车类和小鹏汽车类的工厂类即可,这种实现方式有利于业务的扩展。

门面模式

        门面模式(Facade Pattern):也叫外观模式,要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。 门面模式提供一个高层次的接口,使得子系统更易于使用。

适用场景:

        多个子系统类相互之间存在强依赖的关系,通过门面类统一处理复杂关系的调用,外部类调用时,可以调用门面类,无需再调用子系统类。

 门面模式相对来说比较简单,代码实现如下:

定义系统A类,通过A类实现与系统A的通信:

package designmodel.doormodel;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 模拟系统类A
 * 假设A系统类中有方法doA
 **/
public class SystemA {
    public String doA(){
        return "A";
    }
}

定义系统B类,通过B类实现与系统A的通信:

package designmodel.doormodel;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 模拟系统类B
 * 假设A系统类中有方法doB
 **/
public class SystemB {
    public String doB(){
        return "B";
    }
}

定义系统C类,通过C类实现与系统A的通信:

package designmodel.doormodel;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 模拟系统类C
 * 假设A系统类中有方法doC
 **/
public class SystemC {
    public String doC(){
        return "C";
    }
}

定义门面类,通过门面类实现A、B、C三个系统之间的复杂调用:

package designmodel.doormodel;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 门面模式中的门面类 可以通过此类实现系统A、B、C之间的复杂关系调用
 **/
public class ControlClas {

    //私有化三个系统的类
    private SystemA systemA=new SystemA();
    private SystemB systemB=new SystemB();
    private SystemC systemC=new SystemC();

    //通过此方法实现A、B、C之间的复杂调用
    public void doSomthing(){
        //自定义逻辑
        systemA.doA();
        //自定义逻辑
        systemB.doB();
        //自定义逻辑
        systemC.doC();
    }

}

         我们可以看到,通过最后的control类,我们可以在其中控制系统A、B、C之间相互调用的复杂场景,而在外部的类去调用A、B、C之间实现业务的时候,可以直接调用门面类去实现,无需在A、B、C中定义复杂代码。

装饰器模式

        装饰器模式是一种 对象结构型模式 ,它通过一种无须定义子类的方式来给对象动态增加职责/功能,使用对象之间的关联关系取代类之间的继承关系。 

 应用场景如下:

   举例说明:

       例如五菱汽车,五菱汽车首先能跑,这种特性是属于不变的特性。

       而其他的型号则是不同的,例如续航不同、是否敞篷、是否有智能语音等等。

       针对不变的特性可以定义为接口,针对变化的特性可以定义装饰器,装饰器就是在原有的基础上增加了一些修饰。

代码结构如下:

  1. 定义五菱汽车抽象类,通用方法
  2. 定义敞篷版、gameboy版本继承至五菱汽车抽象类,重写通用方法。
  3. 定义五菱的装饰器类,继承至五菱汽车抽象类,传入五菱汽车抽象类型的对象,调用五菱汽车抽象类型的通用方法。
  4. 分别定义五菱汽车的敞篷装饰器和续航增强装饰器,继承至五菱汽车的装饰器类,重写通用方法,增加装饰器功能。
  5. 通过测试类调用,new 五菱敞篷对象,传入敞篷装饰器,调用装饰器的run方法。

定义五菱新能源汽车的抽象类,定义run方法:

package designmodel.decoratemodel;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 五菱新能源汽车
 **/
public abstract class WulingNewEngeryCar {

    abstract void run();
}

定义五菱新能源的具体型号,例如gameboy,继承至五菱新能源类:

package designmodel.decoratemodel;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 五菱gameBoy
 **/
public class WulingGameBoy extends WulingNewEngeryCar {
    @Override
    void run() {
        System.out.println("五菱gameBoy");
    }
}

定义五菱新能源的具体型号,例如敞篷版,继承至五菱新能源类:

package designmodel.decoratemodel;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 五菱敞篷版
 **/
public class Wulingchangpeng  extends  WulingNewEngeryCar{
    @Override
    void run() {
        System.out.println("敞篷版五菱");
    }
}

 定义五菱新能源汽车的装饰类,装饰类也继承自新能源类:

package designmodel.decoratemodel;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 定义五菱汽车的装饰器类
 **/
public abstract class WulingDecorate extends WulingNewEngeryCar{
    //私有的对象
    private WulingNewEngeryCar wulingNewEngeryCar;
    //公共的构造函数
    public WulingDecorate(WulingNewEngeryCar wulingNewEngeryCar) {
        this.wulingNewEngeryCar = wulingNewEngeryCar;
    }
    //重写汽车的能力
    @Override
    void run() {
        wulingNewEngeryCar.run();
    }
}

定义五菱敞篷版的装饰类 ,继承自五菱汽车的装饰类:

package designmodel.decoratemodel;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 敞篷装饰器
 **/
public class NoDoorDecorate extends WulingDecorate{

    //调用父类的构造方法
    public NoDoorDecorate(WulingNewEngeryCar wulingNewEngeryCar) {
        super(wulingNewEngeryCar);
    }

    //增加装饰
    @Override
    void run() {
        super.run();
        System.out.println("增加敞篷功能");
    }
}

定义五菱续航版本的装饰类 ,继承自五菱汽车的装饰类:

package designmodel.decoratemodel;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 续航增强装饰器
 **/
public class RunLongDecorate extends WulingDecorate{
    public RunLongDecorate(WulingNewEngeryCar wulingNewEngeryCar) {
        super(wulingNewEngeryCar);
    }

    @Override
    void run() {
        super.run();
        System.out.println("续航增强");
    }

}

测试类:

package designmodel.decoratemodel;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 测试类
 **/
public class AppTestClass {
    public static void main(String[] args) {
        //new 一个wuling敞篷类
        Wulingchangpeng wulingchangpeng=new Wulingchangpeng();
        //调用敞篷装饰器 增加敞篷功能
        NoDoorDecorate noDoorDecorate = new NoDoorDecorate(wulingchangpeng);
        noDoorDecorate.run();
        RunLongDecorate runLongDecorate1 = new RunLongDecorate(wulingchangpeng);
        runLongDecorate1.run();
        System.out.println("*****************");
        WulingGameBoy wulingGameBoy = new WulingGameBoy();
        RunLongDecorate runLongDecorate = new RunLongDecorate(wulingGameBoy);
        runLongDecorate.run();
    }
}

执行结果如下:

呕心整理Java中的12种常用设计模式以及应用场景_第9张图片

        我们可以看到,装饰器模式的优势在于,封装不变的run方法,然后基于run方法进行增强,满足在不同场景下针对某些方法进行不同方式的增强,假如又新增了一个型号,那么只需要新增型号类和具体的装饰器类即可,大大减少了代码的耦合,同时又满足方法的增强。

责任链模式

        责任链设计模式是软件开发中常见的一种设计模式,在该模式中将一个请求进行递进处理,从而形成一条链,在链条的节点中处理请求,一个节点处理完自己的逻辑再传递到下一个节点中。

应用场景:

         在应对网页端传输的报文信息时,会对报文信息进行处理,有一些类负责处理消息中的特殊字符,有一些类负责处理处理消息中的敏感词之类的信息,这种情况适用于责任链模式。

代码结构如下:

  1. 定义过滤的父类
  2. 定义两个子类继承父类,重写过滤方法
  3. 定义责任链类,将两个子类传递进去,调用子类的过滤方法。
  4. 通过测试类将子类传递,调用过滤方法。

 定义处理消息的接口:

package designmodel.responsiblity;

/**
 * @Author luocong
 * @Description //TODO
 * @Date 11:06 2022/11/9
 * @Param 
 * @return
 * 定义处理消息的接口
 **/
public interface FatherFilter {
    //定义处理消息的方法
    public String doFilter(String msg);
}

定义过滤特殊字符的类,实现处理消息的接口:

package designmodel.responsiblity;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 处理http消息的类
 * 过滤http消息
 **/
public class HttpFilter implements FatherFilter{

    @Override
    public String doFilter(String msg) {
        String replace = msg.replace("-", "");
        System.out.println("HttpFilter replace msg: "+replace);
        return replace;
    }
}

定义处理敏感词的类,实现处理消息的接口:

package designmodel.responsiblity;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 处理消息的类
 * 过滤消息
 **/
public class MsgFilter implements FatherFilter{

    @Override
    public String doFilter(String msg) {
        String replace = msg.replace("*", "");
        System.out.println("MsgFilter replace msg: "+replace);
        return replace;
    }
}

        定义责任链基类,定义集合,集合中存放泛型为消息接口的对象 ,调用责任链类的时候循环集合,调用对象点过滤方法:

package designmodel.responsiblity;

import java.util.ArrayList;
import java.util.List;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 责任链基类
 **/
public class FatherFilterChain {

    //储存http和msg的filter
    List list=new ArrayList();

    //添加filter
    public void add(FatherFilter fatherFilter){
        list.add(fatherFilter);
    }

    //定义执行过滤的方法
    public void doFilter(String msg){
        for (int i = 0; i < list.size(); i++) {
            msg = list.get(i).doFilter(msg);

        }
    }


}

定义消息类,存放消息:

package designmodel.responsiblity;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 模拟传递消息的类
 **/
public class Msg {
    private String msg;

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public String getMsg() {
        return msg;
    }
}

测试类:

package designmodel.responsiblity;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 测试类
 **/
public class TestResponsiblity {

    public static void main(String[] args) {
        //定义消息类
        Msg msg = new Msg();
        msg.setMsg("你好,------,我是***琪琪.....");
        //new http处理对象和msg处理对象
        HttpFilter httpFilter = new HttpFilter();
        MsgFilter msgFilter = new MsgFilter();
        //new 责任链处理对象
        FatherFilterChain fatherFilterChain = new FatherFilterChain();
        //将http处理对象和msg处理对象加入责任链
        fatherFilterChain.add(httpFilter);
        fatherFilterChain.add(msgFilter);
        //传递消息 执行过滤
        fatherFilterChain.doFilter(msg.getMsg());

    }
}

执行结果如下:

呕心整理Java中的12种常用设计模式以及应用场景_第10张图片

         我们可以看到,所谓的责任链模式,说白了就是利用了多态的特性,将对象封装成集合,在调用方法时循环调用对象的方法,利用循环进行调用,责任链模式的典型应用场景就是spring的filter类,有兴趣的可以看下源码。

享元模式

        享元模式: 通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似对象的开销,从而提高系统资源的利用率,将重复出现的内容作为共享部分取出,由多个对象共享一份,从而减轻内存的压力

应用场景:

        String的实现就是享元模式,底层有针对各种字符的常量池,有变量引用到常量时,就直接引用常量池中的常量,又例如数据库连接池,也是利用享元模式。

下面请看模拟数据库连接的代码实现:

定义数据库连接资源基类 其他类需要继承此类:

package designmodel.enjoymodel;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 定义数据库连接资源基类 其他类需要继承此类
 **/
public abstract class DataSource {
    String dataId;
    String dataName;

    public String getDataId() {
        return dataId;
    }

    public String getDataName() {
        return dataName;
    }

    public void setDataId(String dataId) {
        this.dataId = dataId;
    }

    public void setDataName(String dataName) {
        this.dataName = dataName;
    }

    public DataSource(String dataId, String dataName) {
        this.dataId = dataId;
        this.dataName = dataName;
    }
    public abstract void method();

    @Override
    public String toString() {
        return "DataSource{" +
                "dataId='" + dataId + '\'' +
                ", dataName='" + dataName + '\'' +
                '}';
    }
}

定义数据库对象生产者类:

package designmodel.enjoymodel;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 数据库连接生产者类1
 **/
public class DataSourceMaker extends DataSource{

    public DataSourceMaker(String dataId, String dataName) {
        super(dataId, dataName);
    }

    @Override
    public void method() {
        System.out.println("使用DataSourceMaker1生产数据库连接对象.....");
    }
}

定义工厂类,生产数据库连接对象:

package designmodel.enjoymodel;

import java.util.HashMap;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 数据库连接工厂 生产数据库连接对象
 *
 **/
public class DataSourceFactory {
    private static HashMap hashMap=new HashMap<>();

    public DataSourceFactory() {
        for (int i = 0; i < 10; i++) {
            DataSourceMaker dataSourceMaker = new DataSourceMaker(String.valueOf(i), "DataSource" + i);
            hashMap.put(dataSourceMaker.getDataId(),dataSourceMaker);
        }
    }



    public DataSource getDataSourceFactory(String datasourceName){
        if (hashMap.containsKey(datasourceName)){
            return hashMap.get(datasourceName);
        }
        return null;
    }
}

定义测试类:

package designmodel.enjoymodel;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 **/
public class TestDataSource {

    public static void main(String[] args) {
        //取出数据库连接对象
        DataSourceFactory dataSourceFactory = new DataSourceFactory();
        for (int i = 0; i < 10; i++) {
            System.out.println("i: "+dataSourceFactory.getDataSourceFactory(String.valueOf(i)).toString());
        }
    }
}

执行结果如下:

 呕心整理Java中的12种常用设计模式以及应用场景_第11张图片

         可以看到,享元模式的主旨在于构建一个池子的概念,需要使用对象的时候就从池子中去拿,无需多次创建对象,在数据库连接池的实现中可以看到其身影。

观察者模式

        观察者模式(Observer),又叫发布-订阅模式(Publish/Subscribe),定义对象间一种一对多的依赖关系(注册),使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新(通知)。说白了就是个注册,通知的过程。

代码结构如下:

  1. 定义观察者接口和观察者方法。
  2. 定义接口的实现类A和B
  3. 定义主题类,将观察者传入一个集合内
  4. 调用通知方法,循环观察者,传递参数给观察者做出响应。

        下面使用代码模拟股市在变动时, 游资和基金之间的动向,这种场景能形象生动的展示出观察者模式:

定义股市观察者接口:

package designmodel.watcher;

/**
 * @Author luocong
 * @Description //TODO
 * @Date 12:01 2022/11/9
 * @Param 
 * @return
 * 定义股票观察者父类
 **/
public interface SharsObserver {

    //观察之后做出如何反映
    public void response(int i);
}

定义游资类,实现观察者接口:

package designmodel.watcher;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 个人股民
 **/
public class PersonObserver implements SharsObserver{
    @Override
    public void response(int i) {
        if(i>0){
            System.out.println("游资: 涨了,快点投资投资.......");
        }else{
            System.out.println("游资: 跌了,快点撤资撤资.......");
        }
    }
}

定义基金类,实现观察者接口:

package designmodel.watcher;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 **/
public class CompanyObserver implements SharsObserver {
    @Override
    public void response(int i) {
        if(i>0){
            System.out.println("机构: 涨了,再拉点投资吧.......");
        }else{
            System.out.println("机构: 跌了,稳一点,先不动.......");
        }

    }
}

 定义主题类,将观察者的动态组装在一起:

package designmodel.watcher;

import designmodel.responsiblity.FatherFilter;

import java.util.ArrayList;
import java.util.List;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 定义主题类型
 * 其中定义可以新增删除 通知观察者的方法
 **/
public class Subject {

    //储存游资和基金的对象
    List list=new ArrayList();

    //新增观察者
    public void addObserver(SharsObserver sharsObserver){
        list.add(sharsObserver);
    }

    //通知观察者
    public void change(int j){
        for (int i = 0; i < list.size(); i++) {
            SharsObserver sharsObserver = list.get(i);
            sharsObserver.response(j);
        }
    }
}

测试类:

package designmodel.watcher;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 测试类
 **/
public class TestMain {

    public static void main(String[] args) {
        CompanyObserver companyObserver = new CompanyObserver();
        PersonObserver personObserver = new PersonObserver();
        Subject subject = new Subject();
        subject.addObserver(companyObserver);
        subject.addObserver(personObserver);
        subject.change(2);
        System.out.println("---------------------");
        subject.change(0);

    }
}

执行结果如下:

 呕心整理Java中的12种常用设计模式以及应用场景_第12张图片

         通过代码可以看到,观察者模式类似于责任链模式,所以观察者模式一般配合责任链模式使用。

代理模式

        代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。

静态代理模式

        静态代理就是通过自定义的类,去实现代理过程的一种模式,他只能代理这个类,要想代理其他类,要想代理其他类需要写新的代理方法。

代码结构如下:

  1. 定义汽车接口
  2. 定义汽车接口的实现类,实现汽车接口。
  3. 定义汽车的代理类,实现汽车接口。
  4. 在代理类中注入接口类型的对象,调用run方法,在run方法前后实现新方法的调用

定义汽车接口:

package designmodel.poxymodel.staticpoxy;
/***
 * @Author luocong
 * @Description //TODO
 * @Date 17:02 2022/11/9
 * @Param 
 * @return
 * 定义汽车类
 **/
public interface CarInterface {
    //汽车可以跑
    public void run();
}

定义汽车类,实现汽车接口:

package designmodel.poxymodel.staticpoxy;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 **/
public class Car implements CarInterface {

    @Override
    public void run() {
        System.out.println("汽车在跑.......");
    }
}

定义汽车的代理类,也实现汽车接口:

package designmodel.poxymodel.staticpoxy;

import designmodel.poxymodel.staticpoxy.Car;
import designmodel.poxymodel.staticpoxy.CarInterface;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 定义汽车的代理对象 代理对象也实现汽车接口
 **/
public class Carproxy  implements CarInterface {
    //私有化汽车类
    private Car car;
    //创建构造函数
    public Carproxy(Car car) {
        this.car = car;
    }
    //调用汽车类的run方法 在run之前和之后可以定义新方法
    @Override
    public void run() {
        beforeRun();
        car.run();
        afterRun();
    }

    //汽车运行之前调用的方法
    private void afterRun() {
        System.out.println("汽车打开火了.....");
    }

    //汽车运行之后调用的方法
    private void beforeRun() {
        System.out.println("汽车熄火了.....");
    }
}

测试类: 

package designmodel.poxymodel.staticpoxy;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 测试类
 **/
public class TestMain {
    public static void main(String[] args) {
        Carproxy carproxy = new Carproxy(new Car());
        carproxy.run();
    }
}

执行结果:

 呕心整理Java中的12种常用设计模式以及应用场景_第13张图片

        通过静态代理,我们可以简单的实现对某个类、接口的代理,但是静态代理也有一定的局限性, 如果我们需要对某个新类进行代理时,又需要代理类实现新的接口去重写一些方法,这样显然是不太方便的,所以JDK给我们提供了动态代理的方法。

动态代理模式

        动态代理就是JDK帮我们实现的,其原理是基于类的反射,动态的为我们生成一个代理类,帮我们执行方法,在执行方法的前后位置我们可以定义一些自定义的方法,下面我们来看代码结构:

  1. 定义汽车接口
  2. 定义汽车接口的实现类,实现汽车接口。
  3. 定义代理类,注入接口类型的对象,调用Proxy的newProxyInstence方法,传入对象.getclass.getclassloader,传入对象.getClass.getInterfaces,传入内部类InvocationHandler,在内部类中实现调用。

定义汽车接口:

package designmodel.poxymodel.movepoxy;

/**
 * @Author luocong
 * @Description //TODO
 * @Date 17:15 2022/11/9
 * @Param
 * @return
 * 定义汽车接口
 **/
public interface CarInterface {
    public void run();
}

定义汽车类,实现接口:

package designmodel.poxymodel.movepoxy;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 汽车类实现了汽车接口 重写run方法
 **/
public class Car  implements CarInterface{

    @Override
    public void run() {
        System.out.println("汽车跑起来了......");
    }
}

定义汽车类的代理类(也可以直接在测试类中实现,作用主要就是封装代理过程):

package designmodel.poxymodel.movepoxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 定义汽车的动态代理类
 **/
public class CarMoveProxy {


    //利用JDK的动态代理实现代理模式
    public void doRun(Car car,InvocationHandler invocationHandler){
        //第一个参数 对象.getClass.getClassLoder 第二个参数 对象.getCalss.getInterfaces 第三个参数 invocationHandler内部类
        CarInterface car1= (CarInterface) Proxy.newProxyInstance(car.getClass().getClassLoader(),
                car.getClass().getInterfaces(),
                invocationHandler);
        car1.run();
    }
}

测试类:

package designmodel.poxymodel.movepoxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 测试类
 **/
public class TestProxy {
    public static void main(String[] args) {
        CarMoveProxy carMoveProxy = new CarMoveProxy();
        Car car = new Car();
        carMoveProxy.doRun(car, new InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                System.out.println("汽车启动...");
                Object invoke = method.invoke(car, objects);
                System.out.println("汽车关闭...");
                return invoke;
            }
        });
    }
}

执行结果如下:

 呕心整理Java中的12种常用设计模式以及应用场景_第14张图片

        执行结果和静态代理的结果一致,但是我们可以通过代码了解,唯一的不同就是动态代理无需我们自定义代理类,二是通过java的反射机制为我们动态的生成了一个代理类去执行我们定义的方法。

适配器模式

        将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作

        所谓适配器,就是将不同类型的接口利用某种方法组装在一起,例如我们生活中遇到的数据线转化器,能将不同类型的接口转换为我们需要的接口,适配器模式一般应用于新老项目并存的情况,能将新老项目中不同写法的代码转换。

对象适配器模式

 注入对象,完成适配器转换,代码结构如下: 

  1. 定义接口TYPEC
  2. 定义类TYPEA
  3. 定义适配类,实现接口TYPEC
  4. 填充TypeA的对象为属性,重写接口的方法
  5. 在接口的方法中可以调用typeA的方法

定义typC接口:

package designmodel.adapter.objectadapter;

/**
 * @Author luocong
 * @Description //TODO
 * @Date 18:04 2022/11/9
 * @Param 
 * @return
 * 定义typeC接口
 **/
public interface TypeCInterface {

    //输出5V的电压
    public void v5();
}

定义typeA类:

package designmodel.adapter.objectadapter;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 **/
public class TypeA {
    public void v220(){
        System.out.println("输出220V电压");
    }
}

定义适配器类,实现typeC接口,注入TypeA对象:

package designmodel.adapter.objectadapter;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 定义适配器类 实现typeC接口
 **/
public class PowerAdapter implements TypeCInterface{

    public TypeA typeA=new TypeA();

    public PowerAdapter(TypeA typeA) {
        this.typeA = typeA;
    }

    @Override
    public void v5() {
        typeA.v220();
    }
}

测试类:

package designmodel.adapter.objectadapter;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 **/
public class TestAdapter {
    public static void main(String[] args) {
        PowerAdapter powerAdapter = new PowerAdapter(new TypeA());
        powerAdapter.v5();
    }
}

执行结果:

呕心整理Java中的12种常用设计模式以及应用场景_第15张图片

         可以看到,由于注入了typA对象,在调用v5的方法时,本应该输出5V电压,现在我们可以输出220V电压,这就是对象的适配器模式。

类适配器模式

利用继承和实现完成适配器的转换,代码结构如下:

  1. 定义接口A
  2. 定义类B
  3. 定义适配器类C 继承B 实现接口A
  4. 在适配器类中既可以实现接口A的方法 也可以重写B类的方法

定义TypeC接口:

package designmodel.adapter.classadapter;

/**
 * @Author luocong
 * @Description //TODO
 * @Date 18:04 2022/11/9
 * @Param 
 * @return
 * 定义typeC接口
 **/
public interface TypeCInterface {

    //输出5V的电压
    public void v5();
}

定义typeA类:

package designmodel.adapter.classadapter;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 **/
public class TypeA {
    public void v220(){
        System.out.println("输出220V电压");
    }
}

定义适配器类:

package designmodel.adapter.classadapter;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 定义类适配器 实现接口并继承类 在重写的方法中可以输出220v电压
 * 并且可以定义输出5v电压的逻辑
 **/
public class PowerAdapter extends TypeA implements TypeCInterface{
    @Override
    public void v5() {
        this.v220();
    }

    @Override
    public void v220() {
        super.v220();
    }
}

        可以看到,这两种实现模式最大的差别在于,一个是注入对象实现接口,一个是继承类实现接口,在使用过程中,一般推荐第一种方式,继承在某种程度上来说,会加重类之间的耦合度。 

原型模式

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

        所谓浅拷贝就是将一个对象拷贝成另外一个对象,但是对象中属性的指向是一样的,改变任意一个对象,两个对象的属性跟着变化。

        而深拷贝就是利用IO流将对象copy一份出来,在修改任意对象的属性时,另一对象的属性不会发生变化。

适用场景:

        需要大量使用某个对象

浅拷贝的实现结构如下:

  1. 定义悟空类,定义属性、构造方法、tostring方法、实现cloneable接口
  2. 调用super.clone方法,实现克隆对象
  3. 此时修改对象2,对象1的属性跟着变化

深拷贝的实现结构如下:

  1. 定义悟空类,定义属性、构造方法、tostring方法、实现cloneable接口
  2. 将当前对象写入流中,并通过流再次写出对象。
  3. 此时修改对象2.对象1的属性不会跟着变化了。

浅拷贝的代码如下:

package designmodel.prototype.shallowcopy;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 猴王类 定义悟空的属性 重写克隆方法
 **/
public class KingMonkey implements Cloneable{
    private String name="孙悟空";
    private int age=5000;
    private String skill="七十二变";

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public String getSkill() {
        return skill;
    }

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

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

    public void setSkill(String skill) {
        this.skill = skill;
    }

    public KingMonkey(String name, int age, String skill) {
        this.name = name;
        this.age = age;
        this.skill = skill;
    }

    public KingMonkey() {
    }

    @Override
    public String toString() {
        return "KingMonkey{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", skill='" + skill + '\'' +
                '}';
    }

    //重写clone方法
    @Override
    protected KingMonkey clone() throws CloneNotSupportedException {
        return (KingMonkey)super.clone();
    }
}

深拷贝的代码如下:

package designmodel.prototype.deepcopy;


import java.io.*;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 新的猴王类 将param封装为自己的属性
 **/
public class KingMonkey1 implements Cloneable,Serializable{
    private String name="孙悟空";
    private int age=5000;
    private String skill="七十二变";

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

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

    public void setSkill(String skill) {
        this.skill = skill;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public String getSkill() {
        return skill;
    }

    @Override
    public String toString() {
        return "KingMonkey1{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", skill='" + skill + '\'' +
                '}';
    }

    public KingMonkey1() {
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            //将对象写入字节流
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            //把字节流转化为对象
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            System.out.println("走进来了.....");
            KingMonkey1 kingMonkey1 = (KingMonkey1) ois.readObject();
            return kingMonkey1;
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }
}

模板方法模式

        定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

应用场景:

        例如生成PDF模板,有固定的logo位置、固定的表格排版,不固定的就是数据的解析,可以将logo的位置、基础样式放置在父类,将数据的解析放置在子类。

代码结构如下:

  1. 定义抽象父类,在其中定义属性与方法,把不变的格式封装起来。
  2. 定义子类,继承父类,重写某些方法,加入自己的逻辑,调用父类的总方法,完成处理。
  3. 定义测试类,通过子类调用父类的方法完成逻辑

定义模板类:

package designmodel.templeteModel;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 * 模板类
 **/
public abstract class TempleteClass {
    //基本方法
    protected abstract void doA();
    protected abstract void doB();

    //模板方法
    public void templeteMethod(){
        doA();
        if(isDoAnything()){
            doB();
        }
    }
    //返回布尔值
    public boolean isDoAnything(){
        return true;
    }

}

定义子类的方法实现:

package designmodel.templeteModel;

/**
 * @{NAME}
 * @Description TODO
 * @Author luocong
 * @Date
 * @Version 1.0
 *子类
 **/
public class TempleteChild extends TempleteClass{

    @Override
    protected void doA() {
        System.out.println("进入了A方法");
    }

    @Override
    protected void doB() {
        System.out.println("进入了B方法");
    }

    @Override
    public boolean isDoAnything() {
        return true;
    }

    public void doAllthings(){
        super.templeteMethod();
    }
}

通过模板方法模式,可以把认为是不变部分的算法封装到父类实现,而可变部分的则可以通过继承来继续扩展,行为由父类控制,子类实现。基本方法是由子类实现的,因此子类可以通过扩展的方式增加相应的功能,符合开闭原则。

你可能感兴趣的:(设计模式应用,java,设计模式,代理模式,单例模式,装饰器模式)