【设计模式】 (1)设计模式的七大原则

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

  • 代码重用性(即相同功能的代码,不用多次编写)
  • 可读性(即:编程规范,便于其他人阅读理解)
  • 可靠性(即:当我们增加新功能是,非常方便,对原来的功能没有影响)
  • 使程序实现高内聚,低耦合的特性

设计模式的七大原则

不要问为什么设计模式要这么去设计,这个只是设计模式的一个开发规范,你不遵守也没关系,但是我们应该去遵守这个规范,方便你我他。

单一职责原则

基本介绍

简单的理解就是:一个类只负责一项职责,就像笔者一样,一生只够爱一人,虽然目前还是单身。

注意点

  • 降低类的复杂性,一个类只负责一项职责
  • 提高类的可读性,可维护性
  • 降低变更引起的风险
  • 只有逻辑足够简单,才可以在代码级别违反单一职责原则;只有类中方法只够少,可以再方法级别保持单一原则

接口隔离原则

基本介绍

简单的理解就是:一个类对另一个类的依赖应该建立在最小的接口上。比如说,我是安徽的,安徽是中国的一个省,是依赖于中国的,我是依赖于安徽的,但是这个时候,虽然可以说,我是依赖于中国的,但是,我们不能这么说,因为安徽是我们的依赖关系中最小的那个依赖接口,所以说,我们依赖于安徽(大致是这个意思),看个图:

A会通过接口依赖类B,C会通过接口依赖D,如果接口对于A,C来说不是最小接口的,那么B和D就要去实现他们不需要的方法;

按照隔离原则,A,C分别于他们需要的接口建立依赖关系,也就是采用依赖隔离。

存在的问题以及改进思路

1) 类A通过接口依赖于类B,类C通过接口依赖于D,如果接口对于AC不是最小的接口,那么BD就必须要去实现他们不需要的方法;

2) 将接口拆分为独立的几个接口,AC分别于他们需要的接口建立依赖关系,也就是采用依赖隔离。

3) 效果图如下

依赖倒转(倒置)原则

基本介绍

依赖倒转原则是指:

  • 高层模式不应该依赖底层模式,二者都应该依赖其抽象。
  • 抽象不应该依赖细节,细节应该依赖抽象
  • 依赖倒转的中心思想是面向接口编程
  • 依赖倒转原则是基于这样的设计理念:相比于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在Java中,抽象指的是接口或抽象类,细节就是具体的实现类。
  • 使用接口或抽象类的目的是指定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。

看个实例:

package com.ci123.dependence;

/**
 * Copyright (c) 2018-2028 Corp-ci All Rights Reserved
 * <p> 依赖倒转(倒置)原则
 * Project: design-pattern
 * Package: com.ci123.dependence
 * Version: 1.0
 * <p>
 * Created by SunYang on 2019/11/7 10:14
 */
public class DependenceInversionPrinciple {
    public static void main(String[] args) {
        Person person = new Person();
        person.receive(new EmailIR());
        person.receive(new WeiXinIR());
    }
}

class Email {
    public String getInfo() {
        return "电子邮件信息:Hello,World";
    }
}

/**
 * 完成Person接收消息的功能
 * 方式1 分析:
 * 1. 简单,比较容易想到的
 * 2. 如果我们获取的对象是 微信,短信等,则新增类,同时Person也要增加相应的接收方法
 * 3. 解决思路: 引入一个抽象的接口 IReceiver ,表示接收者 , 这样Person类与接口IReceiver发生依赖
 * 因为Email,WeiXin等属于接收的范围,他们各自实现IReceiver接口就OK,这样我们就符合依赖倒转原则
 */
class Person {
    public void receive(IRceiver iRceiver) {
        System.out.println(iRceiver.getInfo());
    }
}

interface IRceiver {
    String getInfo();
}

class EmailIR implements IRceiver {

    @Override
    public String getInfo() {
        return "电子邮件信息(IReceiver):Hello,World";
    }
}

class WeiXinIR implements IRceiver {

    @Override
    public String getInfo() {
        return "微信信息(IReceiver):Hello,World";
    }
}

依赖关系的三种传递

  • 接口传递
  • 构造方法传递
  • setter方式传递

注意点

  • 低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好
  • 变量的声明类型尽量是抽象类或接口,这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化
  • 集成式遵循里氏替换原则

里氏替换原则

OO中的继承性的思考和说明

  • 继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是设定规范和契约,虽然他不强制要求所有的子类都必须遵守这些契约,但是如果子类对这样已经实现的方法任意修改,就会对整个集成体系造成破坏
  • 继承再给程序设计带来便利的同时,也带来了弊端,比如使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改的时候,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障。
  • 思考:在编程中,如何正确的使用继承?=> 里氏替换原则

基本介绍

  1. 如果对每个类型为T1的对象 o1 ,都有类型为 T2 的对象 o2 ,使得以T1定义的所有程序P在所有的对象 o1 都替换成 o2时,程序P的行为没有发生变化,那么类型T2 是类型T1的子类型,换句话说,所有引用基类的地方必须要透明地使用其子类的对象。
  2. 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
  3. 里氏替换原则告诉我们,继承实际上让两个子类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖,来解决问题。

这里我们看个例子:

package com.ci123.base.liskov;
/**
 * Copyright (c) 2018-2028 Corp-ci All Rights Reserved
 * <p>
 * Project: design-pattern
 * Package: com.ci123.base.liskov
 * Version: 1.0
 * <p> 这里可以看到,我们的B重写了A的func1(int num1,int num2)方法,无意中将减法改成了加法,但是我们的整个继承体系
 * 是已经被破坏了的,会导致继承体系的复用性会比较差,特别是运行多态比较繁琐的时候
 * <p>
 * 改进:
 * 通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖,聚合,组合等关系替代。
 * 即:
 * base <- A
 * base <- B
 * B <<- A
 * <p>
 * Created by SunYang on 2019/11/7 10:55
 */
public class Liskov {
    public static void main(String[] args) {
        A a = new A();
        System.out.println("5-3=" + a.func1(5, 3));
        B b = new B();
        System.out.println("5-3=" + b.func1(5, 3)); // 这里原本是要输出 5-3 的
        System.out.println("/************************************************/");
        AA aa = new AA();
        System.out.println("5-3=" + aa.func1(5, 3));
        BB bb = new BB();
        System.out.println("5-3=" + bb.func(5, 3)); // 这里原本是要输出 5-3 的
    }
}
class Base {
    // 这里放很基础的方法和成员
}
/********************************************************************************************/
class A {
    // 返回两个数的差
    public int func1(int num1, int num2) {
        return num1 - num2;
    }
}
// B 继承A
// 增加一个新的功能,完成两个数相加
class B extends A {
    // 这里重写 A类方法
    @Override
    public int func1(int num1, int num2) {
        // 这里是无意识的重写,是我们不需要的重写,我们本意是要求 减法的
        return num1 + num2;
    }

    public int func2(int num1, int num2) {
        return func1(num1, num2) * 8;
    }
}
/********************************************************************************************/
class AA {

    public int func1(int num1, int num2) {
        return num1 - num2;
    }
}
class BB extends Base {
    // 这里重写 A类方法
    public int func1(int num1, int num2) {
        // 这里是无意识的重写,是我们不需要的重写,我们本意是要求 减法的
        return num1 + num2;
    }

    public int func2(int num1, int num2) {
        return func1(num1, num2) * 8;
    }

    private AA aa = new AA();
    // 这里想要用AA方法
    public int func(int num1 , int num2){
        return this.aa.func1(num1 , num2) ;
    }
}

开闭原则

基本介绍

  • 是编程中最基础,最重要的设计原则
  • 一个软件实体如类,模块核函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节
  • 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化
  • 编程中遵循其他原则,以及使用设计模式的目的就是遵循开闭原则
package com.ci123.base.ocp;

/**
 * Copyright (c) 2018-2028 Corp-ci All Rights Reserved
 * <p>
 * Project: design-pattern
 * Package: com.ci123.base.ocp
 * Version: 1.0
 * <p> 1. 优点比较好理解,简单易操作
 * 2. 缺点是违反了设计模式的OCP原则,即对扩展开放(提供方),对修改关闭(使用方)。即当我们给类增加新功能的时候,尽量不要修改代码,或者尽量少修改代码
 * 3. 如果我们要增加一个新的图形种类,我们要做的修改还是挺多的
 * Created by SunYang on 2019/11/7 11:20
 */
public class OCPDemo {
    public static void main(String[] args) {
        // 使用看看存在的问题
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Rectangle());
        graphicEditor.drawShape(new Circle());
        graphicEditor.drawShape(new Triangle());
    }
}
// 使用方,绘图
class GraphicEditor{
    public void drawShape(Shape shape){
        switch (shape.m_type){
            case 1:
                drawRectangle(shape);
                break;
            case 2:
                drawCircle(shape);
                break;
            case 3:
                drawTriangle(shape);
                break;
            default:
                break;
        }
    }

    private void drawRectangle(Shape shape){
        System.out.println("矩形");
    }
    private void drawCircle(Shape shape){
        System.out.println("圆");
    }
    private void drawTriangle(Shape shape){
        System.out.println("三角形");
    }
}
// 基类
class Shape{
    int m_type ;
}
class Rectangle extends Shape{
    Rectangle(){
        super.m_type = 1 ;
    }
}
class Circle extends Shape{
    Circle(){
        super.m_type = 2 ;
    }
}
// 新增三角形
class Triangle extends Shape{
    Triangle(){
        super.m_type = 3 ;
    }
}

在看看修改后的:

public class OCPDemo {
    public static void main(String[] args) {
        // 使用看看存在的问题
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Rectangle());
        graphicEditor.drawShape(new Circle());
        graphicEditor.drawShape(new Triangle());
        graphicEditor.drawShape(new Other());
    }
}

// 使用方,绘图
class GraphicEditor {
    public void drawShape(Shape shape) {

        shape.draw();
    }

}

// 基类
abstract class Shape {
    int m_type;

    abstract void draw();
}

class Rectangle extends Shape {
    Rectangle() {
        super.m_type = 1;
    }

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

class Circle extends Shape {
    Circle() {
        super.m_type = 2;
    }

    @Override
    void draw() {
        System.out.println("圆");
    }
}

// 新增三角形
class Triangle extends Shape {
    Triangle() {
        super.m_type = 3;
    }

    @Override
    void draw() {
        System.out.println("三角形");
    }
}

class Other extends Shape {
    Other() {
        super.m_type = 4;
    }

    @Override
    void draw() {
        System.out.println("其他的");
    }
}

迪米特法则

基本介绍

  • 一个对象应该对其他对象保持最少的了解
  • 类与类关系密切,耦合度大
  • 迪米特法则又被称为最好知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管对么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public方法,不对外泄露任何信息;
  • 迪米特法则还有个更简单的定义:只与直接朋友通信
  • 直接朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这俩搞个对象之间是朋友关系,耦合方式很多,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量形出现在类的内部。
package com.ci123.base.dp;

import com.sun.org.apache.bcel.internal.generic.NEW;
import com.sun.org.apache.xpath.internal.SourceTree;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;

/**
 * Copyright (c) 2018-2028 Corp-ci All Rights Reserved
 * <p> 迪米特法则
 * Project: design-pattern
 * Package: com.ci123.base.dp
 * Version: 1.0
 * <p>
 * Created by SunYang on 2019/11/7 11:50
 */
// 有一个学校,下属有各个学院和总部,现要求打印出学校总部员工ID和学院员工ID
public class DemeterDemo {
    public static void main(String[] args) {
        // 先创建一个 SchoolManager 对象
        SchoolManager schoolManager = new SchoolManager();

        // 输出学院的员工 ID 和 学校总部的员工信息
        schoolManager.printAllEmployee(new CollegeManager());
    }
}

// 学校总部的员工类
class Employee{
    private String id ;
    public void setId(String id){
        this.id = id ;
    }
    public String getId(){
        return id ;
    }
}
// 学院员工类
class CollegeEmployee{
    private String id ;

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
}
// 管理学院员工的管理类
class CollegeManager{
    // 返回学院的所有员工
    public List<CollegeEmployee> geAllEmployee(){
        List<CollegeEmployee> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            CollegeEmployee employee = new CollegeEmployee();
            employee.setId("学院员工ID=" + i);
            list.add(employee) ;
        }
        return list ;
    }
}
// 分析SchoolManager类的直接朋友有哪些,Employee,CollegeManager
// CollegeEmployee 不是直接朋友关系,而是一个陌生类,这样违背了 迪米特法则
class SchoolManager{
    // 返回学校的总员工
    public List<Employee> getAllEmployees(){
        List<Employee> list = new ArrayList<>();

        for (int i = 0; i < 20; i++) {
            Employee employee = new Employee();
            employee.setId("学校ID=" + i);
            list.add(employee) ;
        }
        return list ;
    }
    // 该方法完成输出学校总部和学院员工信息 ID
    void printAllEmployee(CollegeManager manager){
        // 分析问题
        // 1. 这里的 CollegeEmployee 不是SchoolManager 的直接朋友
        // 2. CollegeEmployee 是以局部变量方式出现在SchoolManager
        // 3. 违反了 迪米特 法则

        // 获取到学院员工
        List<CollegeEmployee> list = manager.geAllEmployee();
        System.out.println("========== 学院员工 ============");
        for (CollegeEmployee employee : list) {
            System.out.println(employee.getId());
        }

        // 获取到学校员工
        List<Employee> allEmployee = this.getAllEmployees();
        System.out.println("========== 学校员工 ============");
        for (Employee employee : allEmployee) {
            System.out.println(employee.getId());
        }
    }
}

改进如下:

// 管理学院员工的管理类
class CollegeManager{
    // 返回学院的所有员工
    public List<CollegeEmployee> geAllEmployee(){
        List<CollegeEmployee> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            CollegeEmployee employee = new CollegeEmployee();
            employee.setId("学院员工ID=" + i);
            list.add(employee) ;
        }
        return list ;
    }
    public void printEmployee(){
        // 获取到学院员工
        List<CollegeEmployee> list = this.geAllEmployee();
        System.out.println("========== 学院员工 ============");
        for (CollegeEmployee employee : list) {
            System.out.println(employee.getId());
        }
    }
}

class SchoolManager{
    // 返回学校的总员工
    public List<Employee> getAllEmployees(){
        List<Employee> list = new ArrayList<>();

        for (int i = 0; i < 20; i++) {
            Employee employee = new Employee();
            employee.setId("学校ID=" + i);
            list.add(employee) ;
        }
        return list ;
    }
    // 该方法完成输出学校总部和学院员工信息 ID
    void printAllEmployee(CollegeManager manager){
        // 封装到 CollegeManager 里面
        manager.printEmployee();

        // 获取到学校员工
        List<Employee> allEmployee = this.getAllEmployees();
        System.out.println("========== 学校员工 ============");
        for (Employee employee : allEmployee) {
            System.out.println(employee.getId());
        }
    }
}

注意点

  • 迪米特法则的核心是降低类之间的耦合
  • 但是注意,由于每个类减少不必要的依赖,因此迪米特法则只是要求降低类间(对象间)耦合关系,并不是要求完全没有依赖关系

合成复用原则

基本介绍

一句话:尽量使用合成 / 聚合的方式 , 而不是使用继承

设计原则核心思想

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

原文地址:https://www.cnblogs.com/sun-iot/p/12198051.html

时间: 2024-10-08 21:25:14

【设计模式】 (1)设计模式的七大原则的相关文章

《Java设计模式》七大原则

最近几年来,人们踊跃的提倡和使用设计模式,其根本原因就是为了实现代码的复用性,增加代码的可维护性.设计模式的实现遵循了一些原则,从而达到代码的复用性及增加可维护性的目的,设计模式对理解面向对象的三大特征有很好的启发,不看设计模式,很难深层地体会到面向对象开发带来的好处 .在刚开始学习中,很难做到将这些模式融汇贯通,所以这个需要我们在编码前多思考,等想充分了,在开始实践编码.下面是设计模式应当遵循的七大原则 1.开闭原则(Open Close Principle) 定义:一个软件实体如类.模块和函

软件设计模式七大原则的含义附举例说明

设计模式(面向对象)有七大原则,分别是: 1.开放-封闭原则 2.单一职责原则 3.依赖倒转原则 4.迪米特法则(也称为最小知识原则) 5.接口隔离原则 6.合成/聚合复用原则 7.里氏代换原则 开放-封闭原则具有理想主义的色彩,他是面向对象设计的终极目标.其他几条则可以看做是开放-封闭原则的实现方法.设计模式就是实现了这些原则,从而达到了代码复用,增加可维护性的目的. 一.开放-封闭原则 概念:一个软件实体如类.模块和函数应该对扩展开放,对修改关闭.模块应该尽量在不修改原代码的情况下进行扩展.

GOF 的23种JAVA常用设计模式总结 03 面向对象七大设计原则

在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,程序员要尽量根据 7 条原则来开发程序,从而提高软件开发效率.节约软件开发成本和维护成本. 各位代码界的大佬们总结出的七大设计原则,还是需要好好了解一下 1.开闭原则 开闭原则(Open Closed Principle,OCP)由勃兰特·梅耶(Bertrand Meyer)提出,他在 1988 年的著作<面向对象软件构造>(Object Oriented Software Construction)中提出:软件实

设计模式七大原则(一)开闭原则

设计模式七大原则--开闭原则 1.1 定义: 一个软件实体如类.模块和函数应该对扩展开放,对修改关闭. 用抽象构建框架,用实体扩展细节. 1.2 优点: 提高软件系统的可复用性及可维护性. 1.3 问题由来: 在软件的生命周期中,因为变化.升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码进过重新测试. 1.4 解决方案: 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化. 实现

图解Java设计模式之设计模式七大原则

图解Java设计模式之设计模式七大原则 2.1 设计模式的目的 2.2 设计模式七大原则 2.3 单一职责原则 2.3.1 基本介绍 2.3.2 应用实例 2.4 接口隔离原则(Interface Segregation Principle) 2.4.1 基本介绍 2.4.2 应用实例 2.5 依赖倒转原则 2.5.1 基本介绍 2.5.2 应用实例 2.6 里氏替换原则 2.6.1 OO中的继承性的思考和说明 2.6.2 基本介绍 2.6.3 一个程序引出的问题和思考 2.6.4 解决方法 2

设计模式的七大原则(Java)

一.OOP三大基本特性 OOP 面向对象程序设计(Object Oriented Programming)作为一种新方法,其本质是以建立模型体现出来的抽象思维过程和面向对象的方法.模型是用来反映现实世界中事物特征的.任何一个模型都不可能反映客观事物的一切具体特征,只能对事物特征和变化规律的一种抽象,且在它所涉及的范围内更普遍.更集中.更深刻地描述客体的特征.通过建立模型而达到的抽象是人们对客体认识的深化. 封装 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的属性和方法只让可信的类操作,

C#设计模式系列:单一职责原则(Single Responsibility Principle)

1.单一职责原则的核心思想 一个类应该有且只有一个变化的原因. 2.为什么要引入单一职责原则 单一职责原则将不同的职责分离到单独的类,每一个职责都是一个变化的中心.当需求变化时,这个变化将通过更改职责相关的类来体现.如果一个类拥有多于一个的职责,则这些职责就耦合到在了一起,那么就会有多于一个原因来导致这个类的变化.对于某一职责的更改可能会损害类满足其他耦合职责的能力.这样职责的耦合会导致设计的脆弱,以至于当职责发生更改时产生无法预期的破坏. 3.单一职责原则的优点 1>.可以降低类的复杂度,一个

设计模式 之 设计的 六大原则(6) 开放封闭原则

  开放封闭原则  定义:一个软件实体如类.模块和函数应该对扩展开放,对修改关闭. 问题由来:在软件的生命周期内,因为变化.升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试. 解决方案:当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化. 开闭原则是面向对象设计中最基础的设计原则,它指导我们如何建立稳定灵活的系统.开闭原则可能是设计模式六项原则中定义最模糊的一个了,

设计模式中的六大设计原则之三,四

求二叉树的宽度和深度 给定一个二叉树,获取该二叉树的宽度和深度. 例如输入 a / \ b c / \ / \ d e f g 返回3. 详细描述: 接口说明 原型: int GetBiNodeInfo(BiNode &head, unsigned int *pulWidth, unsigned int *pulHeight) 输入参数: head 需要获取深度的二叉树头结点 输出参数(指针指向的内存区域保证有效): pulWidth 宽度 pulHeight 高度 返回值: 0 成功 1 失败