设计模式剖析(持续更新中...)

设计模式剖析(持续更新中...),第1张

一、一张图总览

二、23种设计模式 一、创建型(5种) 1、单例模式(8种) 1.1 饿汉式(2种) 1.1.1 静态常量
/**
 * @author zero
 * @description HungrySingleton 饿汉模式
 * @date 2022/4/25 15:09
 */
public class HungrySingleton {

    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1.hashCode());
        System.out.println(instance2.hashCode());
    }

}

class Singleton{
    // 1、构造器私有化
    private Singleton() { }

    // 2、提前创建好对象
    private final static Singleton singleton = new Singleton();

    // 3、提供访问对象的方法
    public static Singleton getInstance() {
        return singleton;
    }
}

优缺点说明:
优点:写法简单,就是在类加载的时候完成对象实例化,避免了线程同步问题
缺点:在类加载的时候完成对象实例化,没有达到懒加载的效果,如果此对象一直未使用,则可能会造成内存浪费

1.1.2 静态代码块
public class HungrySingleton2 {

    public static void main(String[] args) {
        Singleton2 instance1 = Singleton2.getInstance();
        Singleton2 instance2 = Singleton2.getInstance();
        System.out.println(instance1.hashCode());
        System.out.println(instance2.hashCode());
    }

}

class Singleton2{
    private static Singleton2 instance;
    // 1、构造器私有化
    private Singleton2() {}
    static {
        // 2、提前创建好对象
        instance = new Singleton2();
    }
    // 3、提供访问对象的方法
    public static Singleton2 getInstance() {
        return instance;
    }
}
1.2 懒汉式(3种) 1.2.1 线程不安全
public class LazySingletonUnSafe {
    public static void main(String[] args) {
        Singleton instance1 =Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2);
        System.out.println(instance1.hashCode());
        System.out.println(instance2.hashCode());
    }
}

class Singleton{
    private static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        // 在使用的时候才进行实例化
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

优缺点说明:
优点:起到了懒加载的效果
缺点:多线程环境下是不安全的,实际开发种,不推荐这种写法

1.2.2 线程安全
public class LazySingletonSafe {
    public static void main(String[] args) {
        Singleton1 instance1 =Singleton1.getInstance();
        Singleton1 instance2 = Singleton1.getInstance();
        System.out.println(instance1 == instance2);
        System.out.println(instance1.hashCode());
        System.out.println(instance2.hashCode());
    }
}

class Singleton1{
    private static Singleton1 instance;

    private Singleton1() {}

    public static synchronized Singleton1 getInstance() {
        // 加synchronized使得实例化的过程线程安全
        if (instance == null) {
            instance = new Singleton1();
        }
        return instance;
    }
}

优缺点说明:
优点:解决了线程不安全问题
缺点:每次在获取对象实例的时候多要加锁,效率比较低,实际开发种也不推荐这种用法

1.2.3 同步代码块
public class LazySingletonBlock {

    private static LazySingletonBlock instance;

    private LazySingletonBlock() {}

    public static LazySingletonBlock getInstance() {
        if (instance == null) {
            // 此处加锁是不能保证线程安全的
            synchronized (LazySingletonBlock.class) {
                instance = new LazySingletonBlock();
            }
        }
        return instance;
    }
}

说明:
这种同步方式不能起到线程安全的作用

1.3 双重检查
public class DCSingleton {
    public static void main(String[] args) {
        Singleton instance1 =Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2);
        System.out.println(instance1.hashCode());
        System.out.println(instance2.hashCode());
    }
}

class Singleton {
    private static volatile Singleton instance;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

说明:
Double-Check进行了两次if检查,保证了线程安全,即对象只实例化一次,同时也保证了对象的延迟加载,提升效率,在开发种推荐这种方式。

1.4 静态内部类
public class StaticInnerClassSingleton {

    public static void main(String[] args) {
        Singleton instance1 =Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1.hashCode());
        System.out.println(instance2.hashCode());
    }


}
class Singleton{
    private Singleton() {}
    private static class SingletonInstance{
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}

说明:
这种方式采用了类加载的机制来保证初始化实例时只有一个线程
利用静态内部类的特点实现延迟加载,且是线程安全的,推荐使用

1.5 枚举类
public class EnumSingleton {
    public static void main(String[] args) {
        Singleton instance = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;
        System.out.println(instance.hashCode());
        System.out.println(instance2.hashCode());
        instance.say();
    }
}

enum Singleton{
    /**
     * 单例对象
     */
    INSTANCE;
    public void say() {
        System.out.println("this is a enum class");
    }
}

说明:
借助枚举来实现单例模式,不仅能避免线程同步问题,而且还能防止反序列化重新创建新的对象。推荐使用。

2、工厂模式 2.1简单工厂模式

简单工厂包含三种角色:

  • 抽象产品:定义产品的规范,描述了产品的主要特性和功能
  • 具体产品:实现或继承抽象产品的子类
  • 具体工厂:提供了创建产品的方法,调用者通过该方法来创建产品

以造汽车为例,分别建立这三种角色所代表的类。

public abstract class Car {

    public abstract void setCarName();

    public void run() {
        setCarName();
        System.out.println("汽车跑起来!!");
    }
}
public class AudiCar extends Car{
    @Override
    public void setCarName() {
        System.out.println("奥迪汽车");
    }
}
public class BenzCar extends Car {
    @Override
    public void setCarName() {
        System.out.println("奔驰汽车");
    }
}
public class AutoCar extends Car {
    @Override
    public void setCarName() {
        System.out.println("奥拓汽车");
    }
}
public class CarSimpleFactory {

    Car car = null;
    public Car createCar(String brand) {
        if ("Audi".equals(brand)) {
            car = new AudiCar();
        } else if ("Benz".equals(brand)) {
            car = new BenzCar();
        } else {
            car = new AutoCar();
        }
        return car;
    }
}
public class CarStore {
    public static void main(String[] args) {
        CarSimpleFactory factory = new CarSimpleFactory();
        Car audi = factory.createCar("Audi");
        audi.run();
    }
}

优缺点说明:
优点:封装了创建对象的过程,可以通过参数直接获取对象,这样就把对象的创建和业务逻辑分开,降低了代码耦合度,更容易实现扩展
缺点:增加新产品时还需要修改工厂类的代码,如上面代码中if-else,违背了开闭原则

2.2工厂方法模式

工厂方法相比于简单工厂而言,多了一个工厂的抽象,即将创建对象的工厂进行抽象,由不同的实现类型来创建不同的对象。类图如下:

抽象产品类与上边的简单工厂一样,下面展示不同的代码之处:

public abstract class CarFactory {
    public abstract Car createCar(String type);
}
public class AudiCarFactory extends CarFactory{
    @Override
    public Car createCar(String type) {
        Car car = null;
        if ("SUV".equals(type)) {
            car = new AudiSuvCar();
        } else {
            car = new AudiSportCar();
        }
        return car;
    }
}
public class AudiSportCar extends Car {
    @Override
    public void setCarName() {
        System.out.println("奥迪跑车");
    }
}
public class AudiSuvCar extends Car {
    @Override
    public void setCarName() {
        System.out.println("奥迪SUV");
    }
}
public class CarStore {
    public static void main(String[] args) {
        // 创建对象使用具体的工厂
        CarFactory audiCarFactory = new AudiCarFactory();
        Car suv = audiCarFactory.createCar("SUV");
        suv.run();
    }
}
2.3抽象工厂模式

抽象工厂模式是将创建具体类型对象的工厂的共性进行了抽象,形成了更上层的类,同时根据不同类型的工厂提供不同能力的对象创建,与工厂方法模式相比,多了一层抽象,使得不同的业务下沉到抽象工厂,上层提供一个统一的工厂接口,类图如下:

public interface CarFactory {
    Car createCar(String type);
    void modifyCar(Car car);
}
public abstract class CreateAudiCarFactory implements CarFactory {

    @Override
    public Car createCar(String type) {
        Car car = null;
        if ("SUV".equals(type)) {
            car = new AudiSuvCar();
        } else {
            car = new AudiSportCar();
        }
        return car;
    }
}
public class AudiCarFactory extends CreateAudiCarFactory{
    @Override
    public void modifyCar(Car car) {
        System.out.println("修理奥迪汽车!!!");
    }
}
public class CarStore {
    public static void main(String[] args) {
        // 创建对象使用具体的工厂
        CarFactory audiCarFactory = new AudiCarFactory();
        Car suv = audiCarFactory.createCar("SUV");
        suv.run();
        audiCarFactory.modifyCar(suv);
    }
}

抽象工厂模式是工厂方法模式的升级版本,在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。

4、原型模式

原型模式是一个比较简单的模式,在JDK中Object类提供了一个clone()方法,其子类只需要实现Clonealbe接口并重写clone()方法就能实现对象的复制问题,原型模式通过clone方法创建对象是通过复制内存中的流实现的,故在使用时不会调用类的构造函数。这种方式默认提供的复制对象的能力为浅拷贝,如果需要深拷贝对象,则需要定制实现。

public class Person  implements Cloneable{
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
   // 省略get/set/toString方法
    @Override
    protected Person clone() {
        Person person = null;
        try {
            person = (Person) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return person;
    }
}
public class Client {
    public static void main(String[] args) {
        Person person = new Person("Zero", 18);
        Person clone = person.clone();
        System.out.println(person);
        System.out.println(clone);
    }
}

优缺点说明:
优点:性能优良,原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。
缺点:逃避构造函数的约束,这个也不能说是缺点,看实际的使用场景吧

5、建造者模式

建造者模式也叫生成器模式,表示将一个复杂对象的创建与它的表示分开,如创建一个跑车对象,只需要将轮子,方向盘,发动机进行组装即可,无需关心各个组件是如何创建的。建造者模式有四种角色:

  • Product(产品角色):一个具体的产品对象
  • Builder(抽象建造者):创建一个Product对象的各个组件指定的接口/抽象类
  • ConcreBuilder(具体建造者):实现接口,构建和装配各个组件
  • Director(指挥者):构建一个使用Builder接口的对象,它主要是用于创建一个复杂的对象。主要有两个作用:一是隔离了客户与对象的生产过程;二是负责控制产品对象的生产过程
public class House {
    private String basic;
    private String wall;
    private String roofed;
    // 省略get/set方法
}
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 CommonHouse extends HouseBuilder{
    @Override
    public void buildBasic() {
        System.out.println("普通房子打地基3米");
    }

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

    @Override
    public void roofed() {
        System.out.println("普通房子封顶");
    }
}
public class HouseDirector {
    HouseBuilder builder = null;

    public HouseDirector(HouseBuilder builder) {
        this.builder = builder;
    }

    // 具体产品的创建过程,过程是不对外暴露的
    public House constructHouse() {
        builder.buildBasic();
        builder.buildWalls();
        builder.roofed();
        return builder.buildHouse();
    }
}
public class Client {
    public static void main(String[] args) {
        CommonHouse commonHouse = new CommonHouse();
        HouseDirector director = new HouseDirector(commonHouse);
        House house = director.constructHouse();
        System.out.println(house);
    }
}

优缺点说明:
优点:封装性,使用该模式使客户端可以不用知道产品内部组成细节;建造者独立,容易扩展;便于控制细节风险,由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他模块产生影响
缺点:产生多个建造类,有可能造成类爆炸的问题

二、结构型(7种) 1、适配器模式

适配器模式将某个类的接口转换成客户端期望给的另一个接口表示,主要的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。主要分为三类:类适配器模式、对象适配器模式,接口适配器模式。

1.1类适配器模式
public class Voltage220V {
    public int output220v() {
        int src = 220;
        System.out.println("输出220V电压");
        return src;
    }
}
public interface IVoltage5V {
    int output5V();
}
public class VoltageAdapter extends Voltage220V implements IVoltage5V {

    @Override
    public int output5V() {
        int srcV = output220v();
        int dstV = srcV / 44;
        return dstV;
    }
}
public class Phone {
    public void charge(IVoltage5V voltage5V) {
        if (voltage5V.output5V() == 5) {
            System.out.println("电压正常,可以充电。。。。");
        } else {
            System.out.println("电压不正常,无法充电");
        }
    }
}
public class Client {
    public static void main(String[] args) {
        // 类适配器模式
        Phone phone = new Phone();
        phone.charge(new VoltageAdapter());
    }
}
1.2对象适配器模式

根据合成复用原则,在系统中尽量使用关联来替代继承关系,故上边的类适配器模式中的继承不太友好,为了解决此问题,将适配器类中的继承改为持有源接口的实例,以此来符合合成复用原则。

public class VoltageAdapter implements IVoltage5V {
    private Voltage220V voltage220V;

    @Override
    public int output5V() {
        int dstv = 0;
        if (voltage220V != null) {
            int srcV = voltage220V.output220v();
            int dstV = srcV / 44;
        }
        return dstv;
    }

    public void setVoltage220V(Voltage220V voltage220V) {
        this.voltage220V = voltage220V;
    }
}
public class Client {
    public static void main(String[] args) {
        // 对象适配器模式
        Phone phone = new Phone();
        VoltageAdapter voltageAdapter = new VoltageAdapter();
        voltageAdapter.setVoltage220V(new Voltage220V());
        phone.charge(voltageAdapter);
    }
}
1.3接口适配器模式

使用抽象类实现接口的所有方法,同时将抽象类作为适配器类提供给客户端使用,保证了客户端无需实现接口的所有实现,只需实现自己关心的方法即可。

public interface Interface {
    void operation();
    void operation1();
    void operation2();
}
public abstract class AbstractAdapter implements Interface{
    @Override
    public void operation() {

    }

    @Override
    public void operation1() {

    }

    @Override
    public void operation2() {

    }
}
public class Client {
    public static void main(String[] args) {
        // 接口适配器模式
        AbstractAdapter adapter = new AbstractAdapter(){
            @Override
            public void operation() {
                System.out.println("覆盖默认方法实现");
            }
        };
        adapter.operation();
    }
}

优缺点说明:
适配器模式可以让两个没有任何关系的类在一起运行,只要适配器这个角色能够搞定他们就成;增加了类的透明性,提高了类的复用度,灵活性好

2、桥接模式

桥接模式是指将实现与抽象放在两个不同类层次中,使两个类可以独立改变。以不同品牌不同样式手机为例。

public interface Brand {
    void open();
    void call();
    void close();
}
public class Vivo implements Brand{
    @Override
    public void open() {
        System.out.println("Vivo手机开机");
    }

    @Override
    public void call() {
        System.out.println("Vivo手机打电话");
    }

    @Override
    public void close() {
        System.out.println("Vivo手机关机");
    }
}
public class Xiaomi implements Brand{
    @Override
    public void open() {
        System.out.println("小米手机开机");
    }

    @Override
    public void call() {
        System.out.println("小米手机打电话");
    }

    @Override
    public void close() {
        System.out.println("小米手机关机");
    }
}
public abstract class Phone {
    // 聚合了接口业务
    private Brand brand;

    public Phone(Brand brand) {
        this.brand = brand;
    }

    protected void open() {
        this.brand.open();
    }

    protected void call() {
        this.brand.call();
    }

    protected void close() {
        this.brand.close();
    }
}
public class FoldedPhone extends Phone{

    public FoldedPhone(Brand brand) {
        super(brand);
    }

    public void open() {
        super.open();
    }

    public void call() {
        super.call();
    }

    public void close() {
        super.close();
    }
}
public class Client {
    public static void main(String[] args) {
        Phone phone = new FoldedPhone(new Vivo());
        phone.open();
        phone.call();
        phone.close();
        System.out.println("=================");
        Phone phone1 = new FoldedPhone(new Xiaomi());
        phone1.open();
        phone1.call();
        phone1.close();
    }
}

如上代码所示,如果想增加一种样式的手机,只需要继承抽象类Phone后就能实现手机对应的全部功能。同时在JDBC的实现模式中就应用了桥接模式,不同类型数据库实现了相同的JDBC接口业务。

优缺点说明:
抽象和实现分离,优秀的扩展能力,实现细节对客户透明。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://www.54852.com/langs/874491.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-13
下一篇2022-05-13

发表评论

登录后才能评论

评论列表(0条)

    保存