一、设计模式概述

1.1 设计模式介绍

设计模式是在面对同类软件工程设计问题所总结出来的有用的经验,模式不是代码,而是某类问题的通用解决方案,设计模式(Design pattern)代表了最佳的实践。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
设计模式的本质提高 软件的维护性,通用性和扩展性,并降低软件的复杂度

1.2 设计模式目的

编写软件过程中,我们面临着来自 耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性 等多方面的挑战,设计模式是为了让程序(软件),具有更好特性。

代码重用性 (即:相同功能的代码,不用多次编写)
可读性 (即:编程规范性, 便于其他程序员的阅读和理解)
可扩展性 (即:当需要增加新的功能时,非常的方便,称为可维护)
可靠性 (即:当我们增加新的功能后,对原来的功能没有影响)
使程序呈现高内聚,低耦合的特性分享金句:
设计模式包含了面向对象的精髓,“懂了设计模式,你就懂了面向对象分析和设计(OOA/D)的精要”。

1.3 设计模式类型

设计模式分为三种类型,共 23
创建型模式(生产对象):单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式。
结构型模式(功能增强):适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
行为型模式(针对方法):模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter 模式)、状态模式、策略模式、职责链模式(责任链模式)

注意:不同的书籍上对分类和名称略有差别。

二、设计模式七大原则

设计模式原则,其实就是程序员在编程时,应当遵守的原则,也是各种设计模式的基础(即:设计模式为什么这样设计的依据)。

设计模式常用的七大原则有:
单一职责原则
接口隔离原则
依赖倒转(倒置)原则
里氏替换原则
开闭原则
迪米特法则
合成复用原则

2.1 单一职责原则

基本介绍:
对类来说的,即一个类应该只负责一项职责。如类 A 负责两个不同职责:职责 1,职责 2。当职责 1 需求变更而改变 A 时,可能造成职责 2 执行错误,所以需要将类 A 的粒度分解为 A1,A2。
即一个类应该只负责一项职责
示例:

/**
    这里虽然没有在类这个级别上遵守单一职责原则,但是在方法级别上,仍然是遵守单一职责
 */
public class Demo {

    public static void main(String[] args) {
        Vehicle2 vehicle2 = new Vehicle2();
        vehicle2.run("汽车");
        vehicle2.run("飞机");
    }
}

class Vehicle2 {

    public void run(String name) {
        System.out.println(name + " 在公路上运行....");
    }
    public void runAir(String name) {
        System.out.println(name + " 在天空上运行....");
    }
}

单一职责原则注意事项细节
降低类的复杂度,一个类只负责一项职责。
提高类的可读性,可维护性。
降低变更引起的风险

通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则

2.2 接口隔离原则

基本介绍:
客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上

其实通俗来理解就是,不要在一个接口里面放很多的方法,这样会显得这个接口很臃肿不堪。接口应该尽量细化,一个接口对应一个功能模块,同时接口里面的方法应该尽可能的少,使接口更加轻便灵活(例子找美女)

或许看到接口隔离原则这样的定义很多人会觉得和单一职责原则很像,但是这两个原则还是有着很鲜明的区别。接口隔离原则和单一职责原则的审视角度是不同的单一职责原则要求类和接口职责单一注重的是职责,是业务逻辑上的划分,而接口隔离原则要求方法要尽可能的少,是在接口设计上的考虑。例如一个接口的职责包含10个方法,这10个方法都放在一个接口中,并且提供给多个模块访问,各个模块按照规定的权限来访问,并规定了“不使用的方法不能访问”,这样的设计是不符合接口隔离原则的,接口隔离原则要求“尽量使用多个专门的接口”,这里专门的接口就是指提供给每个模块的都应该是单一接口(即每一个模块对应一个接口),而不是建立一个庞大臃肿的接口来容纳所有的客户端访问。
示例:

public class Demo {
    public static void main(String[] args) {
        Searcher2 searcher2 = new Searcher2();
        searcher2.setGirl2(new Girl2("老王"));
        searcher2.show();
    }
}
//接口
interface PettyGirl2{
    // 好看
    void goodLooking();
    // 身材好
    void niceFigure();
}

interface GreatTemperamentGirl{
    // 有气质
    void greatTemperament();
}

class Girl2 implements PettyGirl2,GreatTemperamentGirl{
    private String name;

    public Girl2(String name) {
        this.name = name;
    }

    @Override
    public void goodLooking() {
        System.out.println(this.name+ "好看...");
    }

    @Override
    public void niceFigure() {
        System.out.println(this.name+ "身材好...");
    }

    @Override
    public void greatTemperament() {
        System.out.println(this.name+ "有气质...");
    }
}

// 星探
class Searcher2{
    private Girl2 girl2;

    public void setGirl2(Girl2 girl2) {
        this.girl2 = girl2;
    }
    //展示美女的信息
    public void show(){
        System.out.println("--------美女的信息如下: ---------------");
        //展示面容
        girl2.goodLooking();
        //展示身材
        girl2.niceFigure();
        //展示气质
        girl2.greatTemperament();
    }
}

2.3 依赖倒转原则

基本介绍:
依赖倒转原则(Dependence Inversion Principle)是指:高层模块不应该依赖低层模块,二者都应该依赖其抽象
抽象不应该依赖细节,细节应该依赖抽象
依赖倒转(倒置)的中心思想:是面向接口编程

大致理解:提供一个抽象接口,实现类实现该接口,定义方法的参数为该抽象接口,在使用该方法时传入具体的使用类型(例子:抽取公共方法作为公共类)

依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在 java 中,抽象指的是接口或抽象类,细节就是具体的实现类
使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。

public class Demo {
    public static void main(String[] args) {
        User2 user2 = new User2();
        user2.receive(new Email2());
        user2.receive(new QQ());
    }
}
// 定义接口
interface IReceive{
    public String getInfo();
}

class Email2 implements IReceive{

    public String getInfo(){
        return "email: yingmu...";
    }
}

class QQ implements IReceive{

    @Override
    public String getInfo() {
        return "qq: yimgmu...";
    }
}

class User2{
    public void receive(IReceive receive){
        System.out.println(receive.getInfo());
    }
}

2.4 里氏替换原则

基本介绍
继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。

继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低, 增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障

如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都替换成o2时,程序P的行为没有变化,那么类型T2是类型T1的子类型。
换言之,一个软件实体如果使用的是一个基类的话,那么一定适用于其子类,而且它根本不能察觉出基类对象和子类对象的区别。

比如,假设有两个类,一个是Base类,另一个是Child类,并且Child类是Base的子类。那么一个方法如果可以接受一个基类对象b的话:method1(Base b)那么它必然可以接受一个子类的对象method1(Child c)。

里氏替换原则是继承复用的基石。只有当衍生类可以替换掉基类,软件单位的功能不会受到影响时,基类才能真正的被复用,而衍生类也才能够在基类的基础上增加新的行为

但是需要注意的是,反过来的代换是不能成立的,如果一个软件实体使用的是一个子类的话,那么它不一定适用于基类。如果一个方法method2接受子类对象为参数的话method2(Child c),那么一般而言不可以有method2(b)。

里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能!
注:如果有子类实现了父类方法则调用的就是子类的方法,反之调用的就是父类的方法

public class Demo1 {
    public static void main(String[] args) {
        C c = new C();
        c.add(new B());
    }
}

class C {
    public void add(A a){
        a.fun();
    }
}

class A{
    public void fun(){
        System.out.println("A fun...");
    }
}

class B extends A{
    
    public void fun(){
        System.out.println("B fun...");
    }
    
    public void fun2(){
        System.out.println("B fun2...");
    }
}

2.5 开闭原则

基本介绍
开闭原则(Open Closed Principle)是编程中最基础、最重要的设计原则。
一个软件实体如类,模块和函数应该对扩展开放,对修改关闭。用抽象构建框架,用实现扩展细节。

当软件或需求需要变化时,我们尽量通过扩展实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
创建一个抽象类,并提供一个抽象方法,让子类去实现即可,这样我们有新增功能时,只需要继承抽象类,并实现抽象方法即可,原有的代码就不需要做修改

Java 设计模式委托模式 java设计模式七大原则_System


编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。

/**
 * 开闭原则
 */
public class Demo2 {
    public static void main(String[] args) {
        GraphicEditor2 graphicEditor2 = new GraphicEditor2();
        graphicEditor2.drawShape(new Rectangle2());
        graphicEditor2.drawShape(new Circle2());
    }
}
// 这是一个用于绘图的类
class GraphicEditor2 {
    //接收 Shape 对象,调用 draw 方法
    public void drawShape(Shape2 s2) {
        s2.draw();
    }
}
// 定义抽象类
abstract class Shape2 {
    // 属性
    int m_type;

    // 定义抽象方法
    public abstract void draw();
}
// 矩形
class Rectangle2 extends Shape2 {

    Rectangle2() {
        super.m_type = 1;
    }

    @Override
    public void draw() {
        System.out.println(" 绘制矩形 ");
    }

}
// 圆形
class Circle2 extends Shape2 {

    Circle2() {
        super.m_type = 2;
    }

    @Override
    public void draw() {
        System.out.println(" 绘制圆形 ");
    }
}

2.6 迪米特法则

基本介绍
一个对象应该对其他对象保持最少的了解
类与类关系越密切,耦合度越大。
迪米特法则(Demeter Principle)又叫最少知道原则即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的 public 方法,不对外泄露任何信息(例子电脑关机)
迪米特法则还有个更简单的定义:只与直接的朋友通信。

直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部

public class Demo1 {
    public static void main(String[] args) {
		Person person=new Person();
		person. clickCloseButton();
    }
}
//计算机类
class Computer{
    private void saveCurrentTask(){
        //do something
    }
    private void closeService(){
        //do something
    }
    private void closeScreen(){
        //do something
    }

    private void closePower(){
        //do something
    }

    public void close(){
        saveCurrentTask();
        closeService();
        closeScreen();
        closePower();
    }
}
// 人
class Person{
    private Computer c;

    public  void clickCloseButton(){
        c.close();
    }
}

2.7 合成复用原则

基本介绍
原则是尽量使用合成/聚合的方式,而不是使用继承。

Java 设计模式委托模式 java设计模式七大原则_System_02

2.8 设计原则核心思想

找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
针对接口编程,而不是针对实现编程。
为了交互对象之间的松耦合设计而努力。