设计模式学习记录(持续更新)

185 阅读8分钟

[TOC]


设计模式

学习资源

来源于b站狂神说java设计模式

七大原则

  1. 开闭原则:对拓展开放,对修改关闭。
  2. 里氏替换:继承必须确保超类所拥有的性质在子类中仍然成立。尽量不替换父类的方法
  3. 依赖倒置原则:面向接编程,而不是面向实现编程。
  4. 单一职责原则:控制类的大小、将类解耦、提高内聚性。
  5. 接口隔离原则:为各个类建立他们需要的专用接口。
  6. 迪米特法则:只与你的“朋友交谈”,不与陌生人交流。
  7. 合成复用原则:尽量先使用组合或者聚合等关联关系来使用,而不是继承和实现。

创建型模式 :cat:

单例模式

懒汉式

public class LazyMan {
    private static LazyMan lazyMan;
    private LazyMan(){}
    
    public static LazyMan getInstance() throws InterruptedException {
        if (lazyMan==null){
            lazyMan=new LazyMan();
        }
        return lazyMan;
    }

   
    public static void main(String[] args) throws InterruptedException {
        /**
         * 线程不安全 a,b 同时进入==null
         * 这样会导致都会new
         * 单例创建不成功
         */
        LazyMan a=LazyMan.getInstance();
        LazyMan b=LazyMan.getInstance();

        System.out.println(a.hashCode());
        System.out.println(b.hashCode());
    }
}

饿汉式

public class Hungry {
    private static Hungry hungry = new Hungry();
    private Hungry() {}
    private static Hungry getInstance() {
        return hungry;
    }

    public static void main(String[] args) {
        /**
         * 线程安全,但是较为浪费空间
         * 如果没有用到则会一直占用空间
         */
        Hungry a=Hungry.getInstance();
        Hungry b=Hungry.getInstance();
        
        System.out.println(a.hashCode());
        System.out.println(b.hashCode());
    }
}

双重检查锁

public class DoubleCheckLock {
/**
     * 错误版,没有volatile,会导致可能存在的指令重排
     * 对象初始化过程分为三步
     *      1、分配空间
     *      2、创建对象
     *      3、对象指向空间
     *      correct step is 123,but maybe exist 132,when a process 13,b return a null object  
     */
    private static DoubleCheckLock doubleCheckLock;

    private DoubleCheckLock() {
    }

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

    public static void main(String[] args) {
        DoubleCheckLock a=DoubleCheckLock.getInstance();
        DoubleCheckLock b=DoubleCheckLock.getInstance();

        System.out.println(a.hashCode());
        System.out.println(b.hashCode());
    }
}
//正确版
public class DoubleCheckLock {
    /**
     * 正确版,volatile防止指令重排
     * 第一个volatile内的synchronized,进行线程同步防止指令,这样就能保证第二个if线程安全
     */
    private static volatile DoubleCheckLock doubleCheckLock;

    private DoubleCheckLock() {
    }

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

    public static void main(String[] args) {
        DoubleCheckLock a=DoubleCheckLock.getInstance();
        DoubleCheckLock b=DoubleCheckLock.getInstance();

        System.out.println(a.hashCode());
        System.out.println(b.hashCode());
    }
}

枚举

public enum  EnumF {
    /**
     * 枚举可以保证单例,建议使用
     * 反射安全
     */
    INSTANCE;
    private EnumF(){}
    public static EnumF getInstance(){
        return INSTANCE;
    }

    public static void main(String[] args) {
        EnumF a=EnumF.getInstance();
        EnumF b=EnumF.getInstance();

        System.out.println(a.hashCode());
        System.out.println(b.hashCode());
    }
}

静态内部类

public class StaticClass {
    /**
     * 利用静态内部类可以天然访问外部类的方法
     * 有反射,单例不安全
     */
    private StaticClass(){}
    public static StaticClass getInstance(){
        return InnerClass.staticClass;
    }
    private static class InnerClass{
        private static final  StaticClass staticClass= new StaticClass();
    }
}

工厂模式

厂,生产对象,创建者和调用者分离 不适用new,而使用工厂方法 选择实现类,统一完成对象管理和控制,从而将调用者跟我们的实现者解耦

简单工厂模式(静态工厂模式)

用于创建同一等级结构中的任意产品(对于增加新的产品,需要修改已有代码)

  1. 通过匹配字符串来在工厂里new对象
  2. 消费者与创建对象解耦
  3. 使用时需要修改Factory类的字符串匹配处 违反开闭原则 大多数用的这个,如果要遵守开闭原则,成本较大
  4. 方便调用,有些对象创建很麻烦
public interface Car {
    void name();
}

class Tesla implements Car {
    @Override
    public void name() {
        System.out.println("tesla complete");
    }
}

class Wulin implements Car {
    @Override
    public void name() {
        System.out.println("wulin complete");
    }
}

class CarFactory {
    public static Car getInstance(String carTypeName) {
        switch (carTypeName) {
            case "wulin":
                return new Wulin();
            case "tesla":
                return new Tesla();
            default:
                return null;
        }
    }
}

class Consumer{
    public static void main(String[] args) {
        Car tesla=CarFactory.getInstance("tesla");
        Car wulin=CarFactory.getInstance("wulin");
        tesla.name();
        wulin.name();
    }
}


tesla complete
wulin complete

工厂方法模式

用工厂生产统一等级的固定产品(支持增加任意产品)

  1. 为了符合开闭原则,新增商品类,不需要修改工厂方法,更加符合开闭原则
  2. 将工厂方法也进行抽象,一个商品,对应一个具体的工厂,更符合现实的逻辑,例如丰田车有自己的工厂,本田车也有自己的工厂,而且新增新的商品,不需要修改工厂,只需要新增新的类工厂,符合[^开闭原则拓展性更好]
  3. 类的数量会变得很多,一个商品对应一个工厂
package factory.method;

public interface Car {
    void name();
}

class Tesla implements Car {
    @Override
    public void name() {
        System.out.println("tesla complete");
    }
}

class Wulin implements Car {

    @Override
    public void name() {
        System.out.println("wulin complete");
    }
}

interface CarFactory {
    Car getCar();
}

class WulinCarFactory implements CarFactory {

    @Override
    public  Car getCar() {
        return new Wulin();
    }
}

class TeslaCarFactory implements CarFactory {

    @Override
    public Car getCar() {
        return new Tesla();
    }
}

class Consumer {
    public static void main(String[] args) {
        Car wulin =new WulinCarFactory().getCar();
        Car tesla= new TeslaCarFactory().getCar();

        wulin.name();
        tesla.name();
    }
}

抽象工厂模式

围绕一个超级工厂创建其他工厂,超级工厂又称为其他工厂的工厂,[^套娃??]

  1. 看来并不是套娃,抽象工厂模式,是为了构建一个产品体系并创建体系中的对象

  2. 了解一个概念,产品家族和产品等级,例如中国石油生产93号汽油和天然气,中国石化生产的93号汽油和天然气。 中国石化生产的93号汽油和天然气就是一个产品家族,中国石化生产的93号汽油和中国石化生产的93号汽油是一个同等级的产品

  3. 将具体产品抽象成抽象产品,抽象产品可以构成抽象产品家族,这个家族中的每一员的具体类就是以后要生产的对象。例如中石化和中石油,所生产的产品,抽象出来的抽象产品家族就是93号汽油和天然气。

  4. 抽象工厂依赖于抽象产品家族,一个抽象工厂中有n(n等于抽象产品家族的成员数量)个方法,生产抽象的产品。具体的工厂类实现抽象抽象工厂,生产对应的家族的产品。

  5. 调用方法

    消费者调用

    抽象工厂 抽象工厂对象1=new 具体的产品工厂();

    抽象产品1 具体对象=抽象工厂对象1.get抽象产品1(); ......

    就获得了具体产品

  6. 缺点:如果产品家族经常变动,那么便不适合该模式,因为要频繁修改,太过繁琐,且违反开闭原则

  7. 优点:如果是已经成熟的产品家族,那么抽象工厂模式是一个非常强大的模式,可以随时拓展具体产品家族且创建任意具体产品对象。

package factory.abstractMethod;

//抽象产品家族(体系)>
public interface IPhoneProduct {
    void sendMessage();

    void start();
}

interface IComputerProduct {
    void type();

    void doSomeOffice();
}
//抽象产品家族(体系)<

//苹果具体产品家族 >
class ApplePhoneProduct implements IPhoneProduct {

    @Override
    public void sendMessage() {
        System.out.println("iPhone发短消息了");
    }

    @Override
    public void start() {
        System.out.println("iPhone开机了");
    }
}

class MakBookProduct implements IComputerProduct {
    @Override
    public void type() {
        System.out.println("macBook打字");
    }

    @Override
    public void doSomeOffice() {
        System.out.println("macBook办公");
    }
}
//苹果具体产品家族 <

//小米具体产品家族 >

/**
 * 楼主🐖 刚订购了K30S,有点小激动
 */
class K30S implements IPhoneProduct {
    @Override
    public void sendMessage() {
        System.out.println("小米手机发短信了");
    }

    @Override
    public void start() {
        System.out.println("小米手机开机了");
    }
}

class XiaomiBook implements IComputerProduct {
    @Override
    public void type() {
        System.out.println("小米笔记本打字了");
    }

    @Override
    public void doSomeOffice() {
        System.out.println("小米笔记本办公");
    }
}
//小米具体产品家族 <

//抽象工厂 >
/**
 * 抽象工厂依赖于抽象产品家族
 */
interface AbstractFactory {
    IPhoneProduct getPhone();

    IComputerProduct getComputer();
}
//抽象工厂 <

//苹果具体的工厂 >

/**
 * 继承了 抽象工厂的具体工厂 依赖于继承了 抽象产品家族的具体产品家族
 * 具体的产品工厂负责生产具体产品
 */
class AppleFactory implements AbstractFactory{
    @Override
    public IPhoneProduct getPhone() {
        return new ApplePhoneProduct();
    }

    @Override
    public IComputerProduct getComputer() {
        return new MakBookProduct();
    }
}
//苹果具体的工厂 <

//小米的具体工厂 >
class XiaomiFactory implements AbstractFactory{
    @Override
    public IPhoneProduct getPhone() {
        return new K30S();
    }

    @Override
    public IComputerProduct getComputer() {
        return new XiaomiBook();
    }
}
//小米的具体工厂 <

//消费者 >
class Client{
    public static void main(String[] args) {
        //先创建具体的工厂 抽象工厂-》具体家族工厂 多态
        AbstractFactory appleFactory = new AppleFactory();
        AbstractFactory xiaomiFactory = new XiaomiFactory();

        //再获得具体的产品 抽象产品->具体产品 多态
        IPhoneProduct iPhone=appleFactory.getPhone();
        IComputerProduct mac=appleFactory.getComputer();

        IPhoneProduct k30S=xiaomiFactory.getPhone();
        IComputerProduct xiaomiBook=xiaomiFactory.getComputer();

        iPhone.start();
        iPhone.sendMessage();
        mac.doSomeOffice();
        mac.type();

        k30S.start();
        k30S.sendMessage();
        xiaomiBook.type();
        xiaomiBook.doSomeOffice();
    }
}

//消费者 <
工厂模式类别总结(差异)
简单工厂模式(静态工厂模式)1.适用于同一级别的产品,修改会违反开闭原则,但是因为简单好用,这种模式是使用最多的
抽象方法工1. 适用于同一级别的产品,修改不会影响开闭原则,但是类会变多
抽象工厂模式1. 使用于有产品家族的(产品),不能频繁修改产品家族的成员,但是可以方便的引入新的产品体系。2.前面两个是生产同一级别的产品,是线。抽象工厂是生产成体系的家族产品,是面。3.我更愿意叫抽象工厂模式为抽象家族(体系)产品工厂模式;抽象产品为抽象家族(体系)产品;具体产品为具体家族(体系)产品;抽象工厂为依赖于抽象家族产品的抽象工厂,且厂内有n(n=抽象家族产品的成员数量)个方法;具体工厂为依赖于具体产品的且继承抽象工厂的具体工厂。

建造者模式

建造者模式也是创建者模式的一种,提供了创建对象的最佳方式[^回顾一下建造者模式是帮助创建对象时省new的模式] ,复杂对象,不同表示

生产者生产零件,建造者负责构建???



原型模式



结构型模式​ :cat2:

适配器模式