设计模式七大原则

在这里插入图片描述

一. 前言

1.1 设计模式的重要性

  1. 软件工程中,设计模式(design pattern) 是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案,这个术语由 Erich Gamma 在1990年代从 建筑学 领域引入到 计算机中的

  2. 摩天大厦 vs 简易房

  3. 实际工作经历来说:当一个项目开发完成后,客户提出新增需求,要有一条设计模式 提供扩展性

  4. 人员离职,项目维护性

  5. design patten 在软件中的哪里?

面向对象(OO)  ----> 功能模块[设计模式+算法(数据结构)]  ----> 框架[使用多种设计模式] ---> 架构[集群]

1.2 设计模式的目的

随着系统功能的逐步增加,各种功能相互依赖
再加上团队合作开发,怎么能确保不出现问题?

耦合性 内聚性 可维护性 可扩展性 重用性 灵活性

  • 重用性 :相同的代码只写一遍 ,其他地方引用这里 , 以后修改方便
  • 可读性 :编程规范 团队
  • 可扩展 :增加新功能
  • 可靠性 :新的功能增加,对之前的功能不能造成破坏
  • 高内聚 低耦合

“懂了设计模式,就懂了面向对象分析和设计的精要”

1.3 浅谈七大原则

design pattern 原则,其实就是 各种设计模式的基础
也是程序设计时应当遵守的规则

二.七原则

2.1 单一职责原则

一个类只应该负责一项职责
多个职责的话,修改了其中一个 可能 导致另一个崩溃

package com.ifeng.Princleple.aSingleResponsibility;

public class SingleResponsibility1 {
    public static void main(String[] args) {
        Vehicle vehicle = new Vehicle();
        vehicle.run("Airplane");
        vehicle.run("Car");


    }
}

/*
    run() 违反了单一职责原则
    可以根据工具的不同 分解成不同的类
 */

class Vehicle{
    public void run(String vehicle){
        System.out.println(vehicle + "在公路上运行");
    }
}

改进1

根据交通工具的不同,分解成不同的类即可

public class SingleResponsibility2 {
    public static void main(String[] args) {
        RoadVehicle roadVehicle = new RoadVehicle();
        roadVehicle.run("car");

        AirVehicle airVehicle = new AirVehicle();
        airVehicle.run("Airplane");


    }
}

class RoadVehicle{
    public void run(String vehicle){
        System.out.println(vehicle + "fly in the sky");
    }
}

class AirVehicle{
    public void run(String vehicle){
        System.out.println(vehicle + "fly in the road");
    }
}
改进2

方法级别上 遵循 单一职责
改动小

class Vehicle2{
    /*
    没有太多的改动,只是增加了方法
    虽然这样在类级别上没有遵守singleResponsibility原则,但是在方法上遵守了
     */

    public void run(String vehicle){
        System.out.println(vehicle + "run in the road");
    }

    public void runAir(String vehicle){
        System.out.println(vehicle + "fly in the Air");
    }

    public void runWater(String vehicle){
        System.out.println(vehicle + "swim on the water");
    }

}

    1. 降低类的复杂度,一个类一个职责
    1. 提高可读性 可维护性
    1. 降低变更引起的风险
    1. 只有逻辑拆分的足够简单,

2.2 接口隔离原则

C端不应该 依赖它不需要的接口
一个类对另一个类的依赖应该建立在最小的接口上

在这里插入图片描述

package com.ifeng.Princleple.bInterfaceSegregation;

/**
 * 一个类对另一个类的依赖建立在最小接口上
 * 类A通过interface1依赖classB,classC通过 interface1 依赖 classD,如果 interface1 对于类A 和 类C不是最小接口,那么B D需要实现他们不需要的方法
 * 按照接口隔离原则处理:将interface1拆分成几个接口,Class A & Class C 分别与他们需要的建立关系,也就是接口隔离原则
 *
 */

public class Segregation1 {

}

interface Interface1{
    void operation1();
    void operation2();
    void operation3();
    void operation4();
    void operation5();
}

class B implements Interface1{

    @Override
    public void operation1() {
        System.out.println("B implements func() operation1");
    }

    @Override
    public void operation2() {
        System.out.println("B implements func() operation2");
    }

    @Override
    public void operation3() {
        System.out.println("B implements func() operation3");
    }

    @Override
    public void operation4() {
        System.out.println("B implements func() operation4");
    }

    @Override
    public void operation5() {
        System.out.println("B implements func() operation5");
    }
}

class D implements Interface1{

    @Override
    public void operation1() {
        System.out.println("D implements func() operation1");
    }

    @Override
    public void operation2() {
        System.out.println("D implements func() operation2");
    }

    @Override
    public void operation3() {
        System.out.println("D implements func() operation3");
    }

    @Override
    public void operation4() {
        System.out.println("D implements func() operation4");
    }

    @Override
    public void operation5() {
        System.out.println("D implements func() operation5");
    }
}

// class A 通过interface 依赖使用B,但是只会用到123 方法
class A{
    public void depend1(Interface1 i){
        i.operation1();
    }

    public void depend2(Interface1 i){
        i.operation2();
    }

    public void depend3(Interface1 i){
        i.operation3();
    }

}

// class A 通过interface 依赖使用B,但是只会用到145 方法
class C{

    public void depend1(Interface1 i){
        i.operation1();
    }

    public void depend2(Interface1 i){
        i.operation4();
    }

    public void depend3(Interface1 i){
        i.operation5();
    }

}

//完成后, B D 实现了多余的方法
//解决方式:将interface拆分成多个子接口

改进
package com.ifeng.Princleple.bInterfaceSegregation;

/**
 * 1 interface对于 class A & class C不是最小接口
 * 2. 将interface 根据实际情况 拆分成最小的接口
 */

public class Segregation2 {
}

//根据实际情况 拆分成了三个接口
interface Interface2{
    void operation1();
}
interface Interface3{
    void operation2();
    void operation3();
}
interface Interface4{
    void operation4();
    void operation5();
}

2.3 依赖倒转原则

1.高层模块不应该依赖低层模块,两者都应该依赖其抽象
2.抽象不应该依赖袭击,细节应该依赖抽象
3.依赖倒转(倒置)的中心思想是买男香接口编程
4。依赖倒置的设计理念:相对于细节的多变性,抽象的东西相对稳定。以抽象为基础搭建的架构比以细节为基础的架构要稳定多。
在java中,抽象:接口或抽象类,细节就是具体的实现类
5.使用接口 的 优点:制定好规范,而不需要涉及任何具体的操作。把展现细节的任务交给他们的实现类去完成

public class DependecyInversion {
    public static void main(String[] args) {

        Person person = new Person();
        person.receive(new Email());

    }
}

class Email{
    public String getInfo(){
        return "电子邮件信息:hello email";
    }
}

class Person{
    public void receive(Email email){
        System.out.println(email.getInfo());
    }
}

方式1 分析

  • 1 . 简单
  • 2 . 如果获取的对象是 短信 微信 等,需要新增class 同时Person也要增加相应的接收方法
  • 3 . 引入一个抽象的接口IReceiver,表示接收者,这样Person类与接口IReceiver发生依赖
  • 因为Email WeChat 等属于接收的范围,他们各自实现IReceiver接口就ok , 这就是依赖倒转原则
interface IReceiver{
    public String getInfo();
}

class Email2 implements IReceiver{
    public String getInfo(){
        return "Email";
    }
}

class WeChat implements IReceiver{
    public String getInfo(){
        return "WeChat";
    }
}

2.3.2 依赖传递的是三种方式

2.3.2.1 接口传递
// TODO 1 通过接口传递实现依赖

interface IOpenAndClose{
    public void open(ITV tv);//抽象方法,接收接口
}

interface ITV{//ITV接口
    public void play();
}

class ChangHong implements ITV{
    @Override
    public void play() {
        System.out.println("ChangHong play");
    }
}

//实现接口
class OpenAndClose implements IOpenAndClose{

    @Override
    public void open(ITV tv) {
        tv.play();
    }
}

2.3.2.2 构造方法传递
interface IOpenAndClose2{
    public void open();
}

interface ITV2{
    public void play();
}

class OpenAndClose2 implements IOpenAndClose2{

    public ITV2 itv2;

    // TODO 2 通过构造方法传递依赖
    public OpenAndClose2(ITV2 itv2){
        this.itv2 = itv2;
    }


    @Override
    public void open() {
        this.itv2.play();
    }
}
2.3.2.3 setter 方法传递

interface IOpenAndClose3{
    public void open();

    public void setTv(ITV3 itv3);
}

interface ITV3{
    public void play();
}

class OpenAndClose3 implements IOpenAndClose3{

    private ITV3 itv3;

    @Override
    public void open() {
        this.itv3.play();
    }

    @Override
    public void setTv(ITV3 itv3) {
        this.itv3 = itv3;
    }
}

    1. 低层模块尽量用 抽象类 接口,更稳定
    1. 变量的声明类型尽量是抽象类或接口,变量引用 —缓冲层—实际对象 , 有利于扩展 优化
    1. 继承时 遵循 里氏替换 原则

2.4 里氏替换原则

    1. 继承包含这样一层关系:父类中凡事已经实现好的方法,实际上是在设定规范 和 契约,虽然它不强制要求所有子类遵守
      但是子类任意修改,就会造成系统破坏
    1. 继承 有便利 也有 弊端 。带来入侵性,可移植性降低,增加对象间的耦合
      如果 一个类 被 其他的类所继承,则这个类需要修改的时,必须考虑所有子类,父类修改 可能影响所有子类
    1. 如何使用 继承 ? ====> 里氏替换原则
class A{
    public int func1(int num1,int num2){
        return num1 - num2;
    }

}

class B extends A{

    //无意识重写了func1
    @Override
    public int func1(int num1,int num2){
        return num1 + num2;
    }
    /*
        这里重写了A.func1 ,实际上A 就无效了,整体继承性 复用性 就变差

        解决:
            1) A B 共同抽象出一个更通俗的基类,原有的继承关系去掉,采用依赖,聚合,组合等方式
            2)

     */
    
    public int func2(int num1,int num2){
        return func1(num1,num2) + 9;
    }
}
class Base{
    //更基础的方法写到Base类中
}

class A2 extends Base{
    public int func1(int num1,int num2){
        return num1 - num2;
    }

}

class B2 extends Base{

    public int func1(int num1,int num2){
        return num1 + num2;
    }
    /*
        如果需要B用到A类的方法,使用组合关系
     */
    private A2 a2 = new A2();

    public int fun3(int a,int b){
        return this.a2.func1(a,b);
    }
    public int func2(int num1,int num2){
        return func1(num1,num2) + 9;
    }
}

2.5 开闭原则

    1. 开闭原则 , 编程中最基本的,最重要的设计原则
    1. 模块 函数 英爱对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现构建细节
    1. 当软件需要变化时,尽量扩展实体行为, 而不是通过修改已有的代码来实现
    1. 其他原则设计的目的就是为了遵循开闭原则

在这里插入图片描述

//这是一个用于绘图的类【使用方】
class GraphicEditor{
    //接收Shape对象,然后根据type 来绘制不同的图形
    public void drawShape(Shape s){
        if(s.m_type == 1){
            drawRectangle(s);
        }else if(s.m_type == 2){
            drawCircle(s);
        }else if(s.m_type == 3){
            drawTriangle(s);
        }
    }

    public void drawRectangle(Shape r){
        System.out.println("drawRectangle");
    }

    public void drawCircle(Shape r){
        System.out.println("drawCircle");
    }

    public void drawTriangle(Shape r){
        System.out.println("drawTriangle");
    }
}

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;
    }
}
  • 优点:好理解,容易操作
  • 缺点:违反来设计模式的ocp原则,比如 现在要新增一个 Triangle , GraphicEditor 修改的地方过多
改进
//这是一个用于绘图的类【使用方】
class GraphicEditor2{
    //接收Shape对象,然后根据type 来绘制不同的图形
    public void drawShape(Shape2 s){
        s.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("Rectangle2");
    }
}

class Circle2 extends Shape2{
    Circle2(){
        super.m_type = 2;
    }

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

class Triangle2 extends Shape2{
    Triangle2(){
        super.m_type = 3;
    }

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

2.6 迪米特原则

    1. 一个对象应该对应其他读喜庆保持最少的了解
    1. class & class 关系越紧密,耦合度越高
    1. demeter 又叫 最少知道原则 ,只与直接朋友通信,一个类对自己依赖的类知道的越少越好
      尽量将逻辑疯长在类内部,对除了提供的public方法,不对外泄漏其他信息
    1. 直接朋友:每个对象都会 与 其他对象 存在耦合关系,存在耦合关系 就是 直接朋友
      耦合的方式有很多:依赖 关联 组合 聚合
      我们称出现 成员变量 方法参数 方法返回值 重的类为直接的朋友,
      而出现在局部变量中的类不是直接朋友
      也就是说:陌生的类最好不要以局部变量的形式出现在类的内部
class Employee{
    private String id;

    public void setId(String id){
        this.id = id;
    }

    public String getId(){
        return id;
    }
}

class CollegeEmployee{
    private String id;

    public void setId(String id){
        this.id = id;
    }

    public String getId(){
        return id;
    }
}

class CollegeManager{
    public List<CollegeEmployee> getAllEmployee(){
        List<CollegeEmployee> list = new ArrayList<>();
        for(int i = 0;i < 10; i++){
            CollegeEmployee emp = new CollegeEmployee();
            emp.setId("学院员工 id = " + i);
            list.add(emp);
        }
        return list;
    }

    //TODO 迪米特法则的改进
    public void printEmployee(){
        //获得到学院员工
        List<CollegeEmployee> allEmployee = getAllEmployee();
        System.out.println("------------学院员工-------------");
        allEmployee.forEach(System.out::println);
    }
}

//SchoolManager类的直接朋友类:Employee CollegeManager

class SchoolManager{
    //返回学校总部的员工
    public List<Employee> getAllEmployee() {

        ArrayList<Employee> list = new ArrayList<>();

        for(int i = 0; i < 5; i++){
            Employee emp = new Employee();
            emp.setId("学校总部员工ID = " + i);
            list.add(emp);
        }
        return list;

    }

    // TODO collegeEmployee 不是直接的朋友 而是一个陌生类
    // 陌生类 以 局部变量 的方式出现
    void printAllEmployee(CollegeManager sub){
        //获得到学院员工
//        List<CollegeEmployee> allEmployee = sub.getAllEmployee();
        //TODO 直接把输出学院员工的方法封装到 CollegeManger 中

        sub.printEmployee();

    }

2.7 合成服用原则

在这里插入图片描述

  • 找出应用中可能需要变化之处,把他们独立出来,和 不需要变化的code 区分
  • 针对接口编程,而不是针对实现编程
  • 为交互对象之间的松耦合而设计
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oifengo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值