设计模式(待续)

目录

摘要

GoF创建型模式

单例模式(Singleton) ★★★★☆

模式动机

应用场景

类图

代码示例

原型模式(Prototype Pattern) ★★★★☆

模式动机

应用场景

类图

代码示例

建造者模式(Builder Pattern) ★★☆☆☆

模型动机

应用场景

类图

代码示例


 


摘要

本文对常用设计模式进行了简要的介绍,包含设计模式的类图、简单示例。每一个模式标题上给出了这个设计模式的重要性等级,读者可以根据重要性等级高低来进行学习阅览。

背景:为了让软件系统便于维护,同时又有较好的复用性,在软件设计中应该遵循一些准则,而设计模式就是遵循这些准则而产生的。

面向对象的七大设计原则不是孤立存在的,而是相互依赖,相互补充的。

  • 单一职责原则(Single Responsibility Principle):类的职责要单一,不能将太多的职责放在一个类中。
  • 开闭原则(Open-Closed Principle):软件实体对扩展是开放的,对修改是关闭的。在不修改一个软件实体的基础上去扩展其功能。
  • 里氏代换原则(Liskov Substitution Principle):在软件系统中,一个可以接受基类对象的地方必然可以接受一个子类对象。
  • 依赖倒转原则(Dependency Inversion Principle):要针对抽象层编程,而不是针对具体类编程。
  • 接口隔离原则(Interface Segregation Principle):要使用多个专门的接口来取代一个统一的接口。
  • 合成复用原则(Composite Reuse Principle):在系统中应该尽量多使用组合和聚合关联关系,尽量少使用甚至不使用继承关系。
  • 迪米特法则(Law of Demeter):一个软件实体对其他实体的引用越少越好,如果两个类不必彼此直接通信,那么这两个类就不应当发生直接相互作用,而是通过引入一个第三者发生简介交互。

单一职责原则

一个对象应该只包含单一职责,并且该职责被完整地封装在一个类中,简而言之:一个类只应该有一个引起它变化的原因。一个类有数据职责和行为职责,数据职责通过属性来体现,行为职责则通过方法来体现,如果职责过多,一个职责的变化有可能影响其他职责,导致系统非常脆弱,因此需要将不同职责进行剥离。而单一职责原则就是实现高内聚、低耦合的指导方针。

开闭原则

简单来说,在设计软件时,应当使这个模块在不被修改的前提下被扩展,即实现在不修改源代码的情况下改变这个模块的行为。因为任何软件都需要面临一个很重要的问题,即对它们的需求会随时间的推移而变化,因此我们应该尽量保证系统的设计框架是稳定的,符合开闭原则的软件系统在拥有适应性和灵活性的同时,具备较好的稳定性和延续性。

抽象化便是开闭原则的关键,很多面向对象的编程语言中都提供了接口、抽象类等机制,通过它们定义系统的抽象层,再通过具体类来进行扩展,那么在进行功能拓展时,无需对抽象层进行任何改动,只需要增加具体类来实现新的业务功能即可。里氏代换原则和依赖倒转原则等都是对于开闭原则的具体实现方法。

里氏代换原则

开闭原则需要对系统进行抽象化,而从抽象化到具体化的过程需要使用继承关系和里氏代换原则。简单来说,里氏代换原则是指,所有引用基类的地方必须能透明地使用其子类的对象,但是将子类替换为基类则不一定能够适用。因此,实现里氏代换原则时,应该尽可能地将基类设计为抽象类或者接口,让子类继承父类或实现父接口,这样在扩展新功能时,只需创建新的子类,符合开闭原则。

依赖倒转原则

依赖倒转原则是系统抽象化的具体实现,高层模块不应该低层模块,他们都应该依赖抽象,抽象不应该依赖细节,细节应该依赖抽象。简而言之,要针对接口编程而不是针对实现编程。

具体的做法,即通过尽量引用层次高的抽象层类,适用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等。

接口隔离原则

每一个接口承担一种相对独立的角色,接口隔离原则是对单一职责原则的一种拓展。

合成复用原则

尽量使用对象组合而不是继承来达到复用的目的。这样的目的就是为了在复用时,减少基类对子类带来的影响,减少耦合,通过组合多个已有对象来实现新对象功能。

迪米特法则

迪米特法则主要用于降低系统的耦合度,使类与类之间保持松散的耦合关系。尽量减少一个对象与其陌生对象之间联系,除当前对象本身、作为参数传入当前对象的其他对象,当前对象的成员对象等才是其朋友。转而通过第三方对象来对这些陌生对象进行调度。

GoF创建型模式

单例模式(Singleton) ★★★★☆

模式动机

确保一个类只有一个实例,并对该实例提供全局访问接口。在实现单例模式时,需要考虑多线程访问安全问题。

应用场景

1)在多个客户端会话中共享同一个类的某个实例

2)是某个类只有一个实例对象,或有限个实例对象

类图

在Singleton类内部保存一个静态Singleton实例instance,同时将Singleton的构造方法设置为私有方法,从而外部无法通过构造方法创建Singleton实例,但是外界可以通过Singleton的静态方法getInstance获得Singleton内部保存的单一instance实例。

代码示例

public Singleton {
    
    // 静态成员变量:饱汉式单例模式
    private static Singleton instance = null;
    // 静态成员变量:饿汉式单例模式
    private static Singleton instance = new Singleton();

    // 私有构造方法
    private Singleton(){}

    // 静态公有工厂方法,返回唯一实例
    public static Singleton getInstance() {
        return instance == null ? new Singleton() : instance;
    }
}

在上述代码示例中,有两种声明静态成员变量instance的方法,一种是饱汉式单例模式,另一种是饿汉式单例模式。区别是,饱汉式单例模式,声明instance为空指针,只有在程序调用工厂方法获取实例instance时,才会去创建对象;饿汉式单例模式,则在类加载时就已经把静态成员instance构造好了。

原型模式(Prototype Pattern) ★★★★☆

模式动机

在软件系统中,有时候需要多次创建某一类型的对象,为了简化创建过程,可以只需要创建一个对象,然后使用克隆的方式复制出多个相同的对象。

应用场景

1)创建过程复杂,创建对象频繁时,可以通过给出一个原型对象来指明所要创建的对象类型,然后复制这个原型对象创建更多同类型对象。

类图

Client:客户端成员变量中有Prototype对象,当需要创建Prototype的实例时,访问具体实例的clone方法获取实例。

Prototype:原型类父类。

ConcretePrototype:原型类的具体实现类。

代码示例

/**
* 以具体原型类ConcretePrototype类为例
**/
public class ConcretePrototype implements Cloneable {
    public Object clone() {
        Object object = null;
        try {
            object = super.clone();
        } catch (CloneNotSupportedException e) {
                System.err.println("Not support cloneable");
        }
    }
}

/**
* 使用方法
**/
// 创建原型的实例
ConcretePrototype prototypeA = new ConcretePrototype();
// 拷贝已创建的实例
ConcretePrototype prototypeB = prototypeA.clone();

在Java代码中,所有类的基类java.lang.Object类提供了一个clone()方法,可以将一个Java对象复制一份,因此可以直接调用基类Object提供的clone()方法。需要注意的是,能够实现拷贝的Java类需要继承一个标识接口Cloneable,表示这个类支持复制,否则就会抛出一个CloneNotSupportedException的异常。在实现拷贝方法时,有两种形式,分别为深拷贝和浅拷贝。

浅拷贝:在拷贝时,将A对象的所有成员变量直接赋值给B对象,这样B对象中成员的引用和A对象的引用指向的是相同的内存地址,这时如果对A成员变量引用的内存做修改,也会反映到B对象成员变量中。

深拷贝:在拷贝时,会将A对象所有成员变量做复制,对A对象中引用的对象,会重新声明一个新的内容相同的对象复制给B对象中的成员变量,这时A与B之间相互不会影响。

建造者模式(Builder Pattern) ★★☆☆☆

模型动机

创建者模式是复杂的创建型模式,它用于创建一个包含多个组成部分的复杂对象,可以返回一个完整的产品对象给用户。而用户无需知道创建过程和内部组成细节,只需要直接使用创建好的完整对象即可,这样就使得复杂对象的构建算法和它的表示分离,以便同一个构造算法可以创建不同的表示。

应用场景

1)当某个产品对象拥有一系列成员属性,还有成员对象,这些成员的初始化规则较为复杂,这时产品对象的构建算法需要和产品的组成部分及组装行为分离;

2)当同一个产品构建算法,可以构建不同的产品表示

类图

如上图所示,有Director、Builder、Product三种角色。

Builder(抽象建造者):为创建一个产品Product对象的各个部件指定抽象接口,一般有构建部件抽象方法(buildPartX)和返回构建的产品对象的抽象方法(getResult)。

ConcreteBuilder(具体建造者):具体建造者实现了抽象建造者中的建造方法和返回产品方法。

Product(产品角色):产品角色是被构建的复杂对象,包含多个组成部分,具体建造者创建该产品的内部表示并定义它的装配过程。

Director(指挥者):负责安排对象的建造次序,指挥者与抽象建造者间存在关联关系,可以在其construct()方法中调用建造者对象的部件与装配方法,完成复杂对象的构建,而用户一般只与指挥着Director交互。

代码示例

// 复杂产品对象
@Data
public class Product {
    private String partA;
    private String partB;
    private String partC;
}

// 抽象构建类
public abstract class Builder {
    protected Product product = new Product();
    
    public abstract void buildPartA();
    public abstract void buildPartB();
    public abstract void buildPartC();

    public Product getResult() {
        return product;
    }
}

// 指导者
public class Director {
    private Builder builder;

    public Director(Builder builder) {
        this.builder = builder;
    }

    public void setBuilder(Builder builder) {
        this.builder = builder;
    }

    public Product construct() {
        builder.buildPartA();
        builder.buildPartB();
        builder.buildPartC();
        return builder.getResult();
    }
}


// 用户使用方法
Builder builder = new ConcreteBuilder();
Director director = new Director(builder);
Product product = director.construct();

代码中用户只使用了Director的construct方法,对于产品如何构建并不关心,Director内部封装了相应的建造者,在创建Director时,需要指定具体建造者。Director与具体产品是解耦的,只需要调用建造者的建造方法和获取产品方法来返回产品。

工厂方法模式(Factory Method Pattern) ★★★★★

模型动机

创建某类对象时,需要将对象类型和创建延迟到子类来实现。这样,在创建新类型的对象时,就可以不改变已有的具体工厂类,而可以通过新增一个具体工厂类来生产对应的产品。

 应用场景

1)当某个类不能预先知道他创建的对象的具体类型时

2)当某个类想让子类来指定其创建的对象

类图

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值