0
点赞
收藏
分享

微信扫一扫

【23种设计模式笔记】更新到10(单例,工厂,装饰器,适配器,观察者,外观,状态,策略)

洛茄 2022-03-30 阅读 14

文章目录

1, 单例模式

确保一个类只有一个实例, 而且自行实例化并向整个系统提供这个实例

  • 方法修饰符只能是private
  • 构造方法是private, 并且拥有一个当前类的静态成员变量
  • 提供一个静态方法, 向外界提供当前类的实例 (只能再内部实例)

1.1,饿汉式和懒汉式

饿汉式

  • 直接给你实例
//饿汉式
class Singleton{
    private static Singleton singleton = new Singleton();
    private Singleton(){};
    public static Singleton getInstance(){
        return singleton;
    }
}

懒汉式

  • 需要的时候再实例化
//懒汉式   
class Singleton{
    private static Singleton singleton ;
    private Singleton(){};
    //需要添加同步锁, 防止创建多个实例
    public synchronized static Singleton getInstance(){
        if (singleton!=null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

1.2,懒汉式之双重检查锁

//懒汉式  双重检查锁
class Singleton2{
    //使用双重检查锁进行初始化的实例必须使用Volatile关键字修饰
    private volatile static Singleton2 singleton2 ;
    private Singleton2(){};
    //需要添加同步锁, 防止创建多个实例
    public  static Singleton2 getInstance(){
        if (singleton2==null){
            //缩小同步范围, 等于空的时候再判断是否添加锁
            synchronized (Singleton2.class){
                if (singleton2==null){
                    singleton2 = new Singleton2();
                }
            }
        }
        return singleton2;
    }
}
  • 使用volatile关键字修饰 : 禁止指令重排

Singleton2 singleton2 = new Singleton2();可拆分为3个步骤
1,分配内存 ; 2, 初始化对象, 3 指向刚分配的地址, 若发生重排序
假设 A线程执行了1和3, 还没有执行2, B线程来到判断 null, B线程就会直接返回还没有初始化的instance了.
volatile 可以避免重排序

1.3,单例模式出现多个实例情况

单例模式的实例是全局唯一的, 但是在以下两种情况下, 也会出现多个实例
1, 在分布式系统中, 会有多个JVM虚拟机, 各个虚拟机都有一个实例
2, 同一个虚拟机 , 使用了多个类加载器同事加载这个类, 产生多个实例

1.4,注意点

单例模式最佳实践最好是无状态的

如果单例模式有状态的话, 比如他有一个字段 value = 10 , 在A线程改成了20 , 由于只有一个实例
其他地方获取到的也是这个20 ,

单例模式

2,简单工厂模式(不符合开闭原则)

简单工厂模式(Simple Factory Pattern): 又称为静态工厂方法(Static Factory Method)模式, 他属于类创建型模式.

在简单工厂模式中, 可以根据参数的不同返回不同的实例.

简单工厂模式专门定义一个类来负责创建其他类的实例, 被创建的实例通常都具有共同的父类.

image-20220318202727444

  • 根据参数来创建对应的类

如DateFormat工具类, 获取实例的时候根据传入的参数不同获得不同的子类

image-20220318203057416

例子

/*
简单工厂模式
 */
public class SimpleFactory {
    public  static Product createProduct(String type){
        if (type.equals("A")){
            return new ProductA();
        }else {
            return  new ProductB();
        }
    }

    public static void main(String[] args) {
        Product product = SimpleFactory.createProduct("A");
        product.print();//AAA
    }
}

abstract class Product{
    public abstract void print();
}

class ProductA extends Product{
    @Override
    public void print() {
        System.out.println("AAA");
    }
}
class ProductB extends Product{
    @Override
    public void print() {
        System.out.println("BBB");
    }
}

优点

  • 实现对象的创建和使用分离
    • 创建完全交给专门的工厂去负责
    • 客户端程序员不需要担心创建, 只管使用

缺点

  • 不够灵活, 如果新增一个产品就需要修改工厂,十分麻烦
  • 违反了开闭原则, 没有做到灵活扩展

开闭原则

一个软件实体如类、模块和函数应该对扩展开放,对修改关闭

3, 工厂(方法)模式

工厂模式中, 之前的核心工厂变成了一个抽象接口 , 它负责给出工厂应该实现的方法, 不在负责所有产品的创建, 而是将具体产品的创建工作交给子类去做, 这样就诞生了具体的子工厂(子类: 负责生产具体的产品对象) , 这样就可以将产品类的实例化操作延迟到工厂子类中去完成, 即通过工厂子类来确定究竟应该实例化哪一个具体实例类

现增加一个产品, 不需要修改工厂类, 而创建一个新的子工厂----扩展优于修改

定义

定义一个用于创建对象的接口, 让子类决定实例化哪个类. 工厂方法使一个类的实例化延迟到其子类

image-20220318215124728

例子

public class Factory {
    public static void main(String [] args) {
        FactoryA factoryA=new FactoryA();
        factoryA.createProduct().print();
        FactoryB factoryB=new FactoryB();
        factoryB.createProduct().print();
    }
}
interface AbstractFactory{
    public Product1 createProduct();
}

class FactoryA implements AbstractFactory{
    @Override
    public Product1 createProduct(){
        return new ProductA();
    }
}
class FactoryB implements AbstractFactory{
    @Override
    public  Product1 createProduct(){
        return new ProductB();
    }
}

abstract class Product1 {
    public abstract void print();
}
class ProductA extends Product1 {
    @Override
    public void print(){
        System.out.println("ProductA正在打印!");
    }
}
class ProductB extends Product1 {
    @Override
    public void print(){
        System.out.println("ProductB正在打印!");
    }
}

4,抽象工厂模式(不符合开闭原则)

  • 工厂模式
    • 新增一个工厂, 需要新增一个对应的工厂类, 每个具体工厂都负责生产一种对应的具体产品
    • 工厂模式要求所有的产品都属于同一大类 (指他们都继承了同一抽象类或实现了同一个接口)
  • 抽象工厂模式
    • 可以生产多个大类的产品

image-20220328232447481

  • 对比工厂模式, 就多了一个大类,
  • 如果只有一个产品体系的话, 就会退化成工厂模式, 相当于工厂模式的拓展
  • 缺点
    • 和简单工厂一样, 增加一个新产品体系, 必须要对工厂类修改, 修改工厂逻辑,包括抽象工厂以及所有具体工厂

例子

//抽象工厂
public interface AbstractFactory {
    Phone createPhone(String param);
    Mask createMask(String param);
}
//具体工厂
class SuperFactory implements AbstractFactory{
    @Override
    public Phone createPhone(String param) {
        return new HwPhone();
    }

    @Override
    public Mask createMask(String param) {
        return new N95();
    }
}

//产品大类:  手机
interface Phone{}
class HwPhone implements Phone{}
//产品大类:  口罩
interface Mask{}
class N95 implements Mask{}

5,装饰器模式

举个栗子:

image-20220328234020456

  • 我有个机器人, 能唱歌对话放音乐

有一天, 这个机器人功能不够用了, 我想要更多的功能, 比如拖地跳舞

方式一: 通知厂家对第一代进行升级修改, 最终研制出了第二代产品

image-20220328234247982

  • 之后厂家卖第二套产品都会有拖地跳舞的功能了

方式二: 自己改造

image-20220328234413916

  • 张三给机器人套了个壳子, 加了胳膊腿, 也实现了拖地和跳舞功能
  • 他灵活的扩展了原有的功能, 而且不需要等待厂家重新研发设计发布

例子小结

这两种方式都可以实现给一个类或对象增加新的功能

  • 第一种称之为继承机制

    • 就是继承一个现有类, 在子类进行扩展功能
  • 第二种称之为关联机制

    • 把一个对象嵌入到另一个对象中, 相当于把机器人嵌入到箱子里来, 给他套一个**壳子**来扩展功能
    • 这个**壳子** 就是我们所说的装饰器 , 所以第二种方式也称之为装饰器模式
  • 区别:

    • 第一种是静态的, 一定要写一个新子类, 对类层级进行扩展
    • 第二种是动态的, 拿到一个对象就可以对其进行扩展, 不需要修改原有的类逻辑

定义:

动态的给一个对象添加一些额外的功能, 就增加功能来说, 装饰器模式比生成子类更加灵活

image-20220328235252937

例子

public class DecoratorPattern {
    public static void main(String[] args) {
        MyRobot myRobot = new MyRobot(new FirstRobot()); //和输入输出流差不多
        myRobot.doSomething();
    }
}
interface Robot {
    public void doSomething();
}

class FirstRobot implements Robot{
    @Override
    public void doSomething() {
        System.out.println("唱歌");
        System.out.println("跳舞");
    }
}

class MyRobot implements Robot{
    private Robot robot;
    public MyRobot (Robot robot){
        this.robot = robot;
    }
    @Override
    public void doSomething() {
        robot.doSomething(); //原有的功能
    }
    public void doElseThing(){ //扩展功能
        robot.doSomething();
        System.out.println("拖地");
        System.out.println("洗碗");
    }
}

输入输出流使用了装饰器模式

image-20220329000855815

6,适配器模式

定义:

  • 将一个类的接口变换成客户端锁期待的另一种接口, 从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作

适配器模式类图

  • 和装饰器模式的区别
    • 适配器实现接口,接口方法是对原来类的变化,也就是适配
    • 和装饰器模式一个区别 是 装饰器模式和扩展类继承了同一个接口

例子

public class AdapterPattern {
    public static void main(String[] args) {
        String translate = new Adapter(new Speaker()).translate();
        System.out.println(translate);//翻译: 我爱你
    }
}
class Speaker{
    public String speak(){
        return "我爱你";
    }
}
//翻译
interface Translator{
    String translate();
}
class Adapter implements Translator{
    //把需要适配的对象传入
    private Speaker speaker;

    public Adapter(Speaker speaker) {
        this.speaker = speaker;
    }

    @Override
    public String translate() {
        String speak = speaker.speak();
        //....做了一些事 比如翻译
        return "翻译: "+speak;
    }
}

7,观察者模式

image-20220329221719790

  • 张三欠了赵四等人一笔钱,
  • 张三是一个讲信用的人
  • 张三一旦有钱了就回去通知各位债主子还钱
  • 张三和债主子之间的依赖关系, 在设计模式中 被称为观察者模式
  • 发布订阅模式
    • 观察者和目标相互知道,而发布者和订阅者不用互相知道

定义:

观察者模式: 定义对象间的一种一对多依赖关系, 使得每当一个对象状态发生改变时, 其相关依赖对象皆能得到通知并被自动更新

例子

import java.util.ArrayList;

public class Observe{
    public static void main(String[] args) {
        ZhangSan zhangSan = new ZhangSan();
        zhangSan.borrow(new Lisi());
        zhangSan.borrow(new WangEr());
        //state状态改变,  由张三决定
        zhangSan.notifyCreated();
    }
}
//借款方
interface Debit {
    //借钱方法
    void borrow(Credit credit);
    //通知还钱的方法
    void notifyCreated();
}

class ZhangSan implements Debit{
    ArrayList<Credit> credits = new ArrayList<>();
    int state = 1;
    @Override
    public void borrow(Credit credit) {
        credits.add(credit);//添加一个观察者对象
    }

    @Override
    public void notifyCreated() {
            credits.forEach(credit -> credit.takeMoney());
    }
}
//要款方
interface Credit{
    void takeMoney();
}

class Lisi implements Credit{

    @Override
    public void takeMoney() {
        System.out.println("l李四来要钱了");
    }
}

class WangEr implements Credit{

    @Override
    public void takeMoney() {
        System.out.println("l王二来要钱了");

    }
}

image-20220329222739643

image-20220329222842737

8,外观模式(不符合开闭原则)

image-20220329223728908

  • 张三经历了一些事情, 想证明自己活着, 需要开四项证明

image-20220329223958396

  • 后面张三变成了给别人开证明的工作人员
  • 赵四想证明自己或者, 只需要找张三开证明就可以了, 张三去给完成四个证明的过程
  • 这个例子类似外观模式, 通过给现有的系统添加一个新接口, 去隐藏掉复杂性

定义:

要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行

外观模式提供一个高层次的接口, 使得子系统更易使用

**缺点: **

  • 不符合开闭原则, 如果子系统扩展的话, 必须修改外观模式的类

image-20220329224404828

例子

public class FacadePattern {
    public static void main(String[] args) {
        //调用者只需要使用外观类来完成四项证明, 不需要关心内部流程
        new Facade().prove();
    }
}
//如四个证明
class SubFlow1{
    boolean isTrue(){return true;}
}
class SubFlow2{
    boolean isTrue(){return true;}
}
class SubFlow3{
    boolean isTrue(){return true;}
}
class SubFlow4{
    boolean isTrue(){return true;}
}
//由外观类完成
class   Facade{
    SubFlow1 s1 =  new SubFlow1();
    SubFlow2 s2 =  new SubFlow2();
    SubFlow3 s3 =  new SubFlow3();
    SubFlow4 s4 =  new SubFlow4();
    boolean prove(){
        return s1.isTrue()&&s2.isTrue()&&s3.isTrue()&&s4.isTrue();
    }
}

9,状态模式

阿里巴巴开发手册中提到了表达异常分支时,少用if-else 方式

image-20220329225348450

  • 卫语句: 在使用分支的时候 , 可以提前范围, 比如下面的isBusy中, 满足了直接返回

定义

允许一个对象在其内部状态改变时改变他的行为, 对象看起来似乎修改了他的类.

其别名为状态对象, 状态模式是一种对象行为型模式

  • 行为和状态是一一对应的
  • 不同的状态关联了不同的行为

image-20220329225823070

  • 优点
    • 封装了转换规则, 并枚举了可能的状态
    • 把所有的状态有关的行为封装到状态类中, 并且可以方便的扩展新的状态
    • 还可以让多个环境对象共享一个状态对象, 从而减少对象的创建
  • 缺点
    • 会增加系统(状态)类, 和(状态) 对象的个数, 使用不当会导致程序结构或代码的混乱
public class StatePattern {
    public static void main(String[] args) {
        Context ZhangSan = new Context();
        ZhangSan.changeState(new Angry());//修改状态为Angry
        ZhangSan.work();//小yz跑了, 无精打采!
        ZhangSan.changeState(new Happy());//小yz回来了
        ZhangSan.work();//干活有劲! 一个顶俩!

    }
}
//状态
abstract class State{
    abstract void doWork();
}
//几种状态
class Happy extends State{
    @Override
    void doWork() {
        System.out.println("干活有劲! 一个顶俩!");
    }
}
class Angry extends State{
    @Override
    void doWork() {
        System.out.println("小yz跑了, 无精打采!");
    }
}
class Sad extends State{
    @Override
    void doWork() {
        System.out.println("不开心, 啥也不干! ");
        return;
    }
}
class Context{
    //传入状态
    private State state;
    public void changeState(State state) {
        this.state = state;
    }
    public void work(){
        state.doWork();
    }
}

10,策略模式

image-20220329231412633

  • 比如洗衣机有不同的洗衣模式, 我们使用的过程中只需要选择对应的策略, 洗衣机就会根据这个策略去完成操作

定义

定义一组算法, 将每个算法都封装起来, 并且使他们之间可以互换.

策略模式让算法独立于使用他的客户而变化, 也称之为政策模式

image-20220329231733428

  • 状态模式:
    • 状态强调的状态的不同,状态不同,要做的事情不同(开心->犒劳犒劳自己,不开心->请假不上班),聚焦在开心或者不开心上,而开心或者不开心具体要做什么不关心,你不开心明天辞职也可以
    • 状态可以是外部来改变状态, 也可以自己来更改状态
  • 策略模式:
    • 策略强调要做的事情不同会导致做事情的具体步骤不同,强调的是“做的步骤”,即行为、算法本身

例子:

jdk中有一个线程池执行器

image-20220329233326656

  • 创建的时候需要传入一个拒绝执行策略

image-20220329233415230

  • 这四种需要在客户端传入的, 构造方法的时候需要传入, 通过传入的策略来执行不同的逻辑

11, 代理模式

image-20220329233644212

  • 张三是一个世外高手, 只有代理人能见到张三

  • 翠花想送一包中华给张三, 只能通过代理人传达给张三

  • 张三和代理人之间构成一种代理关系, 而翠花通过代理人传访问张三这种行为关系称之为代理模式

  • 代理人控制了张三的访问

另一个栗子:

比如说我们想访问一个类, 而这个类在另外一台服务器上,

这时我们可以创建一个代理对象, 代理对象来建立连接, 远程调用另一台服务器的实现类,最后把实现类返回给我们

定义:

为其他对象提供一种代理, 以控制对这个对象的访问

image-20220329234306806

package mode11_ProxyPattern;

/**
 * 代理模式
 */
public class ProxyPattern {
    public static void main(String[] args) {
        new RealSubjectProxy().doWork();
    }
}
//目标类接口
interface Subject{
    void doWork();
}
//真正目标类
class RealSubject implements Subject{
    @Override
    public void doWork() {

    }
}
//代理类
class RealSubjectProxy implements Subject{
    private RealSubject subject;

    public RealSubjectProxy() {
        try {
            //通过类加载器获取
            this.subject = (RealSubject) this.getClass().getClassLoader().loadClass("mode11_ProxyPattern.RealSubject").newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //可以在代理类中添加一些方法
    public void connect(){
        System.out.println("建立连接");
    }
    public void log(){
        System.out.println("日志记录");
    }
    @Override
    public void doWork() {
        connect();
        subject.doWork();
        log();
    }
}
  • 发现代理模式跟装饰器模式差不多
  • 代理模式
    • 侧重于对代理对象的访问
  • 装饰器模式
    • 侧重于对对象的功能扩展

学习地址(B站子烁)

https://www.bilibili.com/video/BV15V411z7nD

举报

相关推荐

0 条评论