重温23种设计模式(上)

Posted Bella的技术轮子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重温23种设计模式(上)相关的知识,希望对你有一定的参考价值。

封面图

原文出处:http://dwz.date/fDe


设计模式,即软件设计中的“套路”。每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题解决方案的核心,这样,你就能一次又一次的使用该方案而不必做重复的劳动。设计模式大约有20多种,它们使人们可以更加简单方便的复用成功的设计和体系结构,提高系统维护的有效性。与设计模式密切相关的是6大设计原则,那么就从这些设计原则开始设计模式重温之旅吧。


内容有点小多,所以这里分为上中下三篇来讲解。本篇主要讲解设计模式的6大原则,以及创建型模式。


一、6大设计原则

1. 单一职责原则

  • 核心思想:应该有且仅有一个原因引起类的变更

  • 问题描述:假如有类Class1完成职责T1,T2,当职责T1或T2有变更需要修改时,有可能影响到该类的另外一个职责正常工作。

  • 好处:类的复杂度降低、可读性提高、可维护性提高、扩展性提高、降低了变更引起的风险。

  • 需注意: 单一职责原则提出了一个编写程序的标准,用“职责”或“变化原因”来衡量接口或类设计得是否优良,但是“职责”和“变化原因”都是不可以度量的,因项目和环境而异。

单一职责就是让类(或方法)的功能单一,做到术业有且专攻,是不是有点UNIX设计思想的感觉?


 2. 里斯替换原则

  • 核心思想:在使用基类的的地方可以任意使用其子类,能保证子类完美替换基类。

  • 通俗来讲:只要父类能出现的地方子类就能出现。反之,父类则未必能胜任。

  • 好处:增强程序的健壮性,即使增加了子类,原有的子类还可以继续运行。

  • 需注意:如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系 采用依赖、聚合、组合等关系代替继承。


3. 依赖倒置原则

  • 核心思想:高层模块不应该依赖底层模块,二者都该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象;

  • 说明:高层模块就是调用端,低层模块就是具体实现类。抽象就是指接口或抽象类。细节就是实现类。

  • 通俗来讲:依赖倒置原则的本质就是通过抽象(接口或抽象类)使个各类或模块的实现彼此独立,互不影响,实现模块间的松耦合。

  • 问题描述:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。

  • 解决方案:将类A修改为依赖接口interface,类B和类C各自实现接口interface,类A通过接口interface间接与类B或者类C发生联系,则会大大降低修改类A的几率。

  • 好处:依赖倒置的好处在小型项目中很难体现出来。但在大中型项目中可以减少需求变化引起的工作量。使并行开发更友好。


4. 接口隔离原则

  • 核心思想:类间的依赖关系应该建立在最小的接口上

  • 通俗来讲:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。

  • 问题描述:类A通过接口interface依赖类B,类C通过接口interface依赖类D,如果接口interface对于类A和类B来说不是最小接口,则类B和类D必须去实现他们不需要的方法。

  • 需注意:

    • 接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度

    • 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事

    • 为依赖接口的类定制服务。只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。


5. 迪米特原则

  • 核心思想:类间解耦。

  • 通俗来讲: 一个类对自己依赖的类知道的越少越好。自从我们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚。无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。低耦合的优点不言而喻,但是怎么样编程才能做到低耦合呢?那正是迪米特法则要去完成的。


6. 开闭原则

  • 核心思想:尽量通过扩展软件实体来解决需求变化,而不是通过修改已有的代码来完成变化

  • 通俗来讲: 一个软件产品在生命周期内,都会发生变化,既然变化是一个既定的事实,我们就应该在设计的时候尽量适应这些变化,以提高项目的稳定性和灵活性。


设计原则概括:

单一职责原则告诉我们实现类要职责单一;里氏替换原则告诉我们不要破坏继承体系;依赖倒置原则告诉我们要面向接口编程;接口隔离原则告诉我们在设计接口的时候要精简单一;迪米特法则告诉我们要降低耦合。而开闭原则是总纲,他告诉我们要对扩展开放,对修改关闭。

 

二、23种设计模式之创建型模式

1. 单例模式

单例模式确保一个类只有一个实例,自行初始化并向整个系统提供这个实例。一个类只有一个实例减少了内存开支,特别是一个对象频繁创建销毁时。单例模式可细分为2种模式,饿汉式和懒汉式,二者的区别是初始化实例的时间不同,前者是在定义时就初始化,后者是在第一次使用时初始化,这里注意多线程的并发访问。

/**
 * Singleton - hungry man pattern
 */

public class Singleton {
    private static Singleton singleton = new Singleton();

    private Singleton() { }

    public static Singleton getInstance() {
        return singleton;
    }

    public void hello() {
        System.out.println("hello singleton");
    }
}

/**
 * Singleton2 - idler man pattern
 */

class Singleton2 {
    private static Singleton2 singleton = null;

    private Singleton2() { }

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

        return singleton;
    }

    public void hello() {
        System.out.println("hello singleton");
    }
}


2. 工厂方法模式

工厂方法(Factory Method)模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。

重温23种设计模式(上)


// --------------------- man

public interface Man {
    void talk();
}

public class WhiteMan implements Man {
    public void talk() {
        System.out.println("WhiteMan talking");
    }
}

public class BlackMan implements Man {
    public void talk() {
        System.out.println("BlackMan talking");
    }
}

// --------------------- man factory

public interface ManFactory {
    Man create();
}

public class WhiteManFactory implements ManFactory {
    public Man create() {
        return new WhiteMan();
    }
}

public class BlackManFactory implements ManFactory {
    public BlackMan create() {
        return new BlackMan();
    }
}

// --------------------- test code

public static void main(String[] args) throws InterruptedException {
    ManFactory blackFactory = new BlackManFactory();
    ManFactory whiteFactory = new WhiteManFactory();

    Man blackMan = blackFactory.create();
    Man whiteMan = whiteFactory.create();

    blackMan.talk();
    whiteMan.talk();
}


3. 抽象工厂模式

为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类,可以理解为工厂方法的升级版。

重温23种设计模式(上)


// --------------------- two man interface

public interface Male {
    void talk();
}

public interface Female {
    void talk();
}

// --------------------- black man

public class BlackMale implements Male {
    public void talk() {
        System.out.println("BlackMale talking...");
    }
}

public class BlackFemale implements Female {
    public void talk() {
        System.out.println("BlackFemale talking...");
    }
}

// --------------------- white man

public class WhiteMale implements Male {
    public void talk() {
        System.out.println("WhiteMale talking...");
    }
}

public class WhiteFemale implements Female {
    public void talk() {
        System.out.println("WhiteFemale talking...");
    }
}

// --------------------- man factory

public interface Factory {
    Male createMale();
    Female createFemale();
}

public class BlackFactory implements Factory{
    public Male createMale() {
        return new BlackMale();
    }

    public Female createFemale() {
        return new BlackFemale();
    }
}

public class WhiteFactory implements Factory {
    public Male createMale() {
        return new WhiteMale();
    }

    public Female createFemale() {
        return new WhiteFemale();
    }
}

// --------------------- test code

public static void main(String[] args) throws InterruptedException {
    Factory blackFactory = new BlackFactory();
    Factory whiteFactory = new WhiteFactory();

    Male blackMale = blackFactory.createMale();
    Male whiteMale = whiteFactory.createMale();
    Female blackFemale = blackFactory.createFemale();
    Female whiteFemale = whiteFactory.createFemale();

    blackMale.talk();
    whiteMale.talk();
    blackFemale.talk();
    whiteFemale.talk();
}


4. 建造者模式

建造者模式也叫作生成器模式,将一个复杂对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。比如一个User类的属性有name、age、address、email、job...等,如果想创建一个User对象,传入全部的属性有点太长了,这时可以使用建造者模式,传入一个参数就只设置对应属性的值。

重温23种设计模式(上)


/**
 * Builder
 */

public class User {
    private String name;
    private int age;
    private String phone;
    private String address;

    public static class Builder {
        private String name;
        private int age;
        private String phone;
        private String address;

        public Builder(String name, int age{
            this.name = name;
            this.age = age;
        }

        public Builder phone(String phone{
            this.phone = phone;
            return this;
        }

        public Builder address(String address{
            this.address = address;
            return this;
        }

        public User build() {
            return new User(this);
        }
    }

    public User(Builder builder{
        this.name = builder.name;
        this.age = builder.age;
        this.phone = builder.phone;
        this.address = builder.address;
    }

    @Override
    public String toString() 
{
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", phone='" + phone + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

// ----------- test
public static void main(String[] args{
    User user = new User.Builder("luoxn28"23).phone("119").build();

    System.out.print(user);
}


5. 原型模式

原型模式的定义为:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式的核心是一个clone()方法,通过该方法进行对象的复制,Java提供了一个Cloneable来标示这个对象是可拷贝的,为什么说是“标示”呢?这个接口只是一个标记作用,在JVM中具有这个标记的对象才有可能被拷贝。那怎么才能从“有可能被拷贝”到“可以被拷贝”呢?方式就是覆盖clone()方法。


public class Thing implements Cloneable {
    public Thing() {
        System.out.println("constructor thing");
    }

    @Override
    public Thing clone() throws CloneNotSupportedException {
        Thing thing = null;

        thing = (Thing) super.clone();
        return thing;
    }
}

// --------------------- test code

public static void main(String[] args) throws CloneNotSupportedException {
    Thing thing = new Thing();
    Thing cloneThing = thing.clone();
}


剩余的18种设计模式将会在后面的文章中介绍,敬请期待,谢谢(❁´◡`❁)*✲゚*

-END-


专注服务后端开发,分享Java、数据库、中间件、Docker、DDD、计算机基础知识等方面的内容,同时也会分享自己开发路上的点点滴滴,欢迎关注,和Bella酱一起学习一起成为更好的自己!

以上是关于重温23种设计模式(上)的主要内容,如果未能解决你的问题,请参考以下文章

onActivityResult 未在 Android API 23 的片段上调用

大话设计模式之代理模式

重温三种创建单例模式的方式(面试)

重温js——正则表达式

替换的片段仍然可见

[Spring 源解系列] 重温 IOC 设计理念