设计模式-02

举报
kwan的解忧杂货铺 发表于 2024/05/15 06:35:24 2024/05/15
【摘要】 二.具体模式 1.单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。主要优点单例模式提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它。由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。允许可变数目的实例。基于单例模式我们可以进行扩展,使用与单例控制相...

二.具体模式

1.单例模式

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

主要优点

  • 单例模式提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它。
  • 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
  • 允许可变数目的实例。基于单例模式我们可以进行扩展,使用与单例控制相似的方法来获得指定个数的对象实例,既节省系统资源,又解决了单例单例对象共享过多有损性能的问题。

主要缺点

  • 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
  • 单例类的职责过重,在一定程度上违背了“单一职责原则”。因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身的功能融合到一起。
  • 现在很多面向对象语言(如 Java、C#)的运行环境都提供了自动垃圾回收的技术,因此,如果实例化的共享对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致共享的单例对象状态的丢失。

适用场景

  • 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器或资源管理器,或者需要考虑资源消耗太大而只允许创建一个对象。
  • 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。

2.单例模式的实现

饿汉式:

public class EagerSingleton {
    private static final EagerSingleton instance = new EagerSingleton();

    private EagerSingleton() {
    }

    public static EagerSingleton getInstance() {
        return instance;
    }
}

懒汉式:

public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {
    }

    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}
//线程安全版本
public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {
    }

    public synchronized static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

双重检查锁模式:

public class Hand_01_Single {
    private volatile static Hand_01_Single hand01Single;

    public static Hand_01_Single getSingle() {
        if (hand01Single == null) {
            synchronized (Hand_01_Single.class) {
                if (hand01Single == null) {
                    hand01Single = new Hand_01_Single();
                }
            }
        }
        return hand01Single;
    }
}

枚举:

通过枚举实现单例模式的优势包括:

  1. 线程安全:枚举实例的创建是线程安全的,无需担心多线程下的并发访问问题。
  2. 防止反序列化创建新实例:枚举类型默认阻止了反序列化创建新的枚举实例,保证了单例的唯一性。
  3. 简洁明了:实现代码非常简洁,而且枚举在语言层面上就保证了单例的特性。
public enum SingletonEnum {
    INSTANCE;

    // 可以在枚举中添加其他成员变量和方法
    // ...

    // 示例方法
    public void doSomething() {
        // ...
    }
}
// 获取单例实例
SingletonEnum instance=SingletonEnum.INSTANCE;

// 调用方法
        instance.doSomething();

3.工厂模式

  • 简单工厂模式
  • 工厂方法模式
  • 抽象工厂模式

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

抽象程度

简单工厂模式 < 工厂方法模式 < 抽象工厂模式。

角色分类

1、简单工厂模式:抽象产品、具体产品、工厂类。

2、工厂方法模式:抽象产品、具体产品、抽象工厂、具体工厂。

3、抽象工厂模式:抽象产品族、抽象产品、具体产品、抽象工厂、具体工厂。

简单工厂模式

  • 工厂类封装了创建具体产品对象的函数
  • 扩展性非常差,新增产品的时候,需要去修改工厂类。
  • 组成
    • 工厂类(ShoesFactory):工厂模式的核心类,会定义一个用于创建指定的具体实例对象的接口。
    • 抽象产品类(Shoes):是具体产品类的继承的父类或实现的接口。
    • 具体产品类(NiKeShoes\AdidasShoes\LiNingShoes):工厂类所创建的对象就是此具有产品实例。
public interface Sender {

    void send();

}

public class MailSender implements Sender {

    public void send() {
        System.out.println("send mail");
    }
}

public class MessageSender implements Sender {

    public void send() {
        System.out.println("send message !");
    }
}

public class SenderFactory {
    public static Sender getSender(Class<? extends Sender> clazz) {
        if (clazz == null) {
            return null;
        }
        try {
            return clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

public class SendTest {
    public static void main(String[] args) {
        SenderFactory.getSender(MailSender.class).send();
        SenderFactory.getSender(MessageSender.class).send();
    }
}

工厂方法模式

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

public interface Sender {

    void send();

}

public class MailSender implements Sender {

    public void send() {
        System.out.println("send mail");
    }
}

public class MessageSender implements Sender {

    public void send() {
        System.out.println("send message !");
    }
}

public interface SenderFactory {
    Sender getSender();
}

public class MessageSenderFactory implements SenderFactory {

    public Sender getSender() {
        return new MessageSender();
    }
}

public class MailSenderFactory implements SenderFactory {

    public Sender getSender() {
        return new MailSender();
    }
}

public class SendTest {
    public static void main(String[] args) {
        SenderFactory mailSenderFactory = new MailSenderFactory();
        mailSenderFactory.getSender().send();
        SenderFactory messageSenderFactory = new MessageSenderFactory();
        messageSenderFactory.getSender().send();
    }
}

抽象工厂模式

提供一个创建一系列相关或者相互依赖的对象的接口,无须指定他们具体的类。

/**
 * 支付接口
 */
public interface IPay {
    void pay();
}

public class AliPay implements IPay {
    @Override
    public void pay() {
        System.out.println("支付宝支付流程开始:");
    }
}

public class WePay implements IPay {
    @Override
    public void pay() {
        System.out.println("微信支付流程开始:");
    }
}

public class UniPay implements IPay {
    @Override
    public void pay() {
        System.out.println("银联支付流程开始:");
    }
}

public class OverseasPay implements IPay {
    @Override
    public void pay() {
        System.out.println("跨境支付流程开始:");
    }
}

/**
 * 退款接口
 */
public interface IRefund {
    void pay();
}

public class AliRefund implements IRefund {
    @Override
    public void pay() {
        System.out.println("支付宝退款流程开始:");
    }
}

public class WeRefund implements IRefund {
    @Override
    public void pay() {
        System.out.println("微信退款流程开始:");
    }
}

public class UniRefund implements IRefund {
    @Override
    public void pay() {
        System.out.println("银联退款流程开始:");
    }
}

public class OverseasRefund implements IRefund {
    @Override
    public void pay() {
        System.out.println("跨境支付退款流程开始:");
    }
}

public abstract class IPaymentFactory {

    abstract IPay getPayment();

    abstract IRefund getRefund();
}

public class AliPaymentFactory extends IPaymentFactory {
    @Override
    public IPay getPayment() {
        return new AliPay();
    }

    @Override
    IRefund getRefund() {
        return new AliRefund();
    }
}

public class WePaymentFactory extends IPaymentFactory {
    @Override
    public IPay getPayment() {
        return new WePay();
    }

    @Override
    IRefund getRefund() {
        return new WeRefund();
    }
}

public class UniPaymentFactory extends IPaymentFactory {
    @Override
    public IPay getPayment() {
        return new UniPay();
    }

    @Override
    IRefund getRefund() {
        return new UniRefund();
    }
}

public class OverseasPaymentFactory extends IPaymentFactory {
    @Override
    public IPay getPayment() {
        return new OverseasPay();
    }

    @Override
    IRefund getRefund() {
        return new OverseasRefund();
    }
}

public class AbstractFactoryTest {
    public static void main(String[] args) {
        IPaymentFactory paymentFactory = new AliPaymentFactory();
        paymentFactory.getPayment().pay();
        paymentFactory.getRefund().refund();
    }

}

4.模版方法模式

在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。

  • 代码复用

  • 功能扩展

    • AQS-自定义锁

    • Mybatis-BaseExecutor

5.代理模式

意图

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

适用性

在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用 Proxy 模式。下面是一 些可以使用 Proxy 模式常见情况:

远程代理(Remote Proxy )为一个对象在不同的地址空间提供局部代表。NEXTSTEP[Add94] 使用 NXProxy 类实现了这一目的。Coplien[Cop92] 称这种代理为“大使” (Ambassador )。
虚代理(Virtual Proxy )根据需要创建开销很大的对象。在动机一节描述的 ImageProxy 就是这样一种代理的例子。 保护代理(Protection Proxy )控制对原始对象的访问。保护代理用于对象应该有不同
的访问权限的时候。例如,在 Choices 操作系统[ CIRM93]中 KemelProxies 为操作系统对象提供 了访问保护。 智能指引(Smart Reference
)取代了简单的指针,它在访问对象时执行一些附加操作。它的典型用途包括: 对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它(也称为 SmartPointers[Ede92 ] )。

当第一次引用一个持久对象时,将它装入内存。

在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。

代理模式 与 装饰器模式:

对于两个模式,首先要说的是,装饰模式就是代理模式的一个特殊应用,两者的共同点是都具有相同的接口,不同点则是代理模式着重对代理过程的控制,而装饰模式则是对类的功能进行加强或减弱,它着重类的功能变化

image-20230730200220085

6.命令模式

命令是对命令的封装,每一个命令都是一个操作,请求方发出请求,接收方接收请求,并执行操作。命令模式解耦了请求方和接收方,命令模式属于行为型模式

  • 接收者角色(Receiver):负责具体执行一个请求
  • 命令角色(ICommand):定义需要执行的所有命令行为
  • 具体的命令角色(ConcreteCommand):内部维护一个 Receiver
  • 请求者角色(Invoker):接收客户端的命令,并执行命令

优点

  • 通过引入命令的抽象接口,实现了命令请求与实现的解耦
  • 扩展性良好,可以很容易的增加新命令

缺点

  • 命令类可能过多

意图

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。

适用性

抽象出待执行的动作以参数化某对象,你可用过程语言中的回调(call back)函数表达这种参数化机制。所谓回调函数是指函数先在某处注册,而它将在稍后某个需要的时候被调用。Command 模式是回调机制的一个面向对象的替代品。

在不同的时刻指定、排列和执行请求。一个 Command 对象可以有一个与初始请求无关的生存期。如果一个请求的接收者可用一种与地址空间无关的方式表达,那么就可将负责该请求的命令对象传送给另一个不同的进程并在那儿实现该请求。

支持取消操作。Command 的 Excute 操作可在实施操作前将状态存储起来,在取消操作时这个状态用来消除该操作的影响。Command 接口必须添加一个 Unexecute 操作,该操作取消上一次 Execute
调用的效果。执行的命令被存储在一个历史列表中。可通过向后和向前遍历这一列表并分别调用 Unexecute 和 Execute 来实现重数不限的“取消”和“重做”。

支持修改日志,这样当系统崩溃时,这些修改可以被重做一遍。在 Command 接口中添加装载操作和存储操作,可以用来保持变动的一个一致的修改日志。从崩溃中恢复的过程包括从磁盘中重新读入记录下来的命令并用 Execute 操作重新执行它们。

用构建在原语操作上的高层操作构造一个系统。这样一种结构在支持事务( transaction)的信息系统中很常见。一个事务封装了对数据的一组变动。Command 模式提供了对事务进行建模的方法。Command
有一个公共的接口,使得你可以用同一种方式调用所有的事务。同时使用该模式也易于添加新事务以扩展系统。

命令模式 与 策略模式:

命令模式和策略模式的类图确实很相似,只是命令模式多了一个接收者(Receiver)角色。
策略模式的意图是封装算法,它认为“算法”已经是一个完整的、不可拆分的原子业务;而命令模式则是对动作的解耦,把一个动作的执行分为执行对象(接收者角色)、执行行为(命令角色),让两者相互独立而不相互影响。

image-20230730200316819

7.责任链模式

意图

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

适用性

有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。

你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。

可处理一个请求的对象集合应被动态指定。

8.装饰模式

意图

动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator 模式相比生成子类更为灵活。

适用性

在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

处理那些可以撤消的职责。

当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

9.策略模式

意图

定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

适用性

许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。

需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间/时间权衡的算法。当这些变体实现为一个算法的类层次时[H087] ,可以使用策略模式。

算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。

一个类定义了多种行为, 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的 Strategy 类中以代替这些条件语句。

//状态机支持的全部三种transition方式。
StateMachineBuilder<States, Events, Context> builder=StateMachineBuilderFactory.create();
        //external transition
        builder.externalTransition()
        .from(States.STATE1)
        .to(States.STATE2)
        .on(Events.EVENT1)
        .when(checkCondition())
        .perform(doAction());

        //internal transition
        builder.internalTransition()
        .within(States.STATE2)
        .on(Events.INTERNAL_EVENT)
        .when(checkCondition())
        .perform(doAction());

        //external transitions
        builder.externalTransitions()
        .fromAmong(States.STATE1,States.STATE2,States.STATE3)
        .to(States.STATE4)
        .on(Events.EVENT4)
        .when(checkCondition())
        .perform(doAction());

        builder.build(machineId);

        StateMachine<States, Events, Context> stateMachine=StateMachineFactory.get(machineId);
        stateMachine.showStateMachine();

10.适配器模式

意图

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

适用性

你想使用一个已经存在的类,而它的接口不符合你的需求。

你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。

(仅适用于对象 Adapter )你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。

解释说明:

  • Target 目标角色 该角色定义把其他类转换为何种接口,也就是我们的期望接口,例子中的 IUserInfo 接口就是目标角色。
  • Adaptee 源角色 你想把谁转换成目标角色,这个“谁”就是源角色,它是已经存在的、运行良好的类或对象,经过适配器角色的包装,它会成为一个崭新、靓丽的角色。
  • Adapter 适配器角色

只有当项目中没有时间做新的接口的时候,才临时使用适配器。适配器这个东西就是为补救应急而生的。

比如说,我们有一个接口,传入两个参数,name,age。现在这个接口发生了改变,需要传 3 个参数,name,age,sex。这个时候我们做一个适配器。Adapter 里边多加一个参数,多组装一个新的 bean。

image-20230730191100023

11.原型模式

意图

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

适用性

当要实例化的类是在运行时刻指定时,例如,通过动态装载;或者

为了避免创建一个与产品类层次平行的工厂类层次时;或者

当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。

12.观察者模式

意图

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。

适用性

当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。

当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。

当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。

13.门面模式

意图

为子系统中的一组接口提供一个一致的界面,Facade 模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

适用性

当你要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类。这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。Facade
可以提供一个简单的缺省视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过 facade 层。

客户程序与抽象类的实现部分之间存在着很大的依赖性。引入 facade 将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。

当你需要构建一个层次结构的子系统时,使用 facade 模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,你可以让它们仅通过 facade 进行通讯,从而简化了它们之间的依赖关系。

中介者模式 与 门面模式:

门面模式为复杂的子系统提供一个统一的访问界面,它定义的是一个高层接口,该接口使得子系统更加容易使用,避免外部模块深入到子系统内部而产生与子系统内部细节耦合的问题。中介者模式使用一个中介对象来封装一系列同事对象的交互行为,它使各对象之间不再显式地引用,从而使其耦合松散,建立一个可扩展的应用架构。

image-20230730200252414

14.享元模式

意图

运用共享技术有效地支持大量细粒度的对象。

适用性

一个应用程序使用了大量的对象。

完全由于使用大量的对象,造成很大的存储开销。

对象的大多数状态都可变为外部状态。

如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。

应用程序不依赖于对象标识。由于 Flyweight 对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。

15.备忘录模式

备忘录模式就是一个对象的备份模式,提供了一种程序数据的备份方法

  • 需要保存和恢复数据的相关状态场景。
  • 提供一个可回滚(rollback)的操作
  • 需要监控的副本场景中。
  • 数据库连接的事务管理就是用的备忘录模式

image-20230730200436371

16.解释器模式

建议看看就得了,开发中无使用场景。(性能问题严重;类膨胀问题)

  1. 计算器开发;
  2. 人工智能,语法解析

在设计模式中,解释器模式(Interpreter Pattern)是一种行为型模式,它用于定义一种语言的文法规则,并且通过解释器来解释处理该语言中的表达式。这种模式通常用于处理复杂的语法或表达式,并将其转换为可执行的操作或执行结果。

解释器模式包含以下主要组成部分:

  1. 抽象表达式(Abstract Expression):定义一个抽象接口,包含一个 interpret()方法,用于解释和处理表达式。
  2. 终结符表达式(Terminal Expression):实现抽象表达式接口,表示语言中的终结符,即不能再进一步解释的元素。
  3. 非终结符表达式(Non-terminal Expression):实现抽象表达式接口,表示语言中的非终结符,可以通过递归解释其他表达式。
  4. 上下文(Context):包含待解释的语言表达式。
  5. 解释器(Interpreter):对语言进行解释和处理的类,根据表达式的文法规则进行解释。
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。