设计模式概述以及七大设计原则

2024-04-23 10:48

本文主要是介绍设计模式概述以及七大设计原则,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 什么是设计模式
  • 设计模式的作用
  • 设计模式使用原则
  • 设计模式七大原则
      • 单一职责原则
      • 接口隔离原则
      • 依赖倒置原则
      • 里氏替换原则
      • 开闭原则
      • 最少知道原则
      • 合成复用原则

什么是设计模式

设计模式是指经过多年编程实践验证的,针对面向对象语言的一套有用的编程模式。

设计模式的作用

正确的使用设计模式可以降低代码间的耦合层度,便于代码的扩展以及维护。

设计模式使用原则

使用设计模式前应该明白:

  • 设计模式是有用的,前提是你在正确的场景下使用正确的设计模式.23种设计模式针对23种不同的场景,应该根据自己的场景来选择使用设计模式。
  • 设计模式的使用可能是有利有弊的,比如在保证系统的可扩展性的同时,可能导致类个数的快速膨胀,因此不要为了使用设计模式而使用,避免模式滥用。
  • 设计模式的背后是七大设计原则,七大设计原则的背后就是一个字:分!!

设计模式七大原则

单一职责原则

每个方法,每个类,每个框架都只做一件事.尽可能的切割方法,类,避免将不同的功能代码块放在一个函数中.
这样也就要求方法尽可能的短,类尽可能的精简.根据马丁福勒的理论:一个函数甚至不应该超过6行,虽然我觉得有点太极端,但是往这方面追求总是没错的。

  • 场景:
    读取文件然后判断其中有多少个字符,应该将加载文件和分割字符分在两个函数,便于后面变化带来的需求调整.如果需要判断单词个数,句子个数,可以复用加载文件的函数.

接口隔离原则

在定义接口的时候,要尽量小,将真正需要放在一起的接口放在一起.比如我写一个动物的接口:

interface Animal{void eat();void fly();void swim();
}

这个接口明显没有达到接口隔离的标准,因为并不是所有的动物都会飞,都会游泳,所以最好的方式就是:

interface Eatable{void eat();
}
interface Fliable{void fly();
}
interface Swimable{void swim();
}

这样就将接口之间隔离开来,不定义总接口.这也暗合了Go中的接口尽量小的设计思想.

依赖倒置原则

上层代码不应该依赖下层,他们都应该依赖抽象.上层是指调用其他方法的是上层,被其他方法调用的是下层.
反例:

public class Dog {public Dog() {}void eat() {System.out.println("Dog is eating...");}
}public class Person {private Dog dog;public Person(Dog dog) {this.dog = dog;}void feed() {System.out.println("Person starts to feed dog...");this.dog.eat();}
}public class Main {public Main() {}public static void main(String[] args) {Dog dog = new Dog();Person person = new Person(dog);person.feed();}
}

上述代码可以完成人喂狗的操作,但是如果有一天,想要人喂猫,应该怎么处理呢?如果沿着上述代码的思路,就是:

public class Cat {void eat() {System.out.println("Cat is eating...");}
}public class Dog {void eat(){System.out.println("Dog is eating...");}
}public class Person {private Dog dog;private Cat cat;public Person(Dog dog) {this.dog = dog;}public Person(Cat cat) {this.cat = cat;}void feedDog() {System.out.println("Person starts to feed dog...");dog.eat();}void feedCat() {System.out.println("Person starts to feed cat...");cat.eat();}
}public class Main {public static void main(String[] args){Dog dog = new Dog();Person person = new Person(dog);person.feedDog();Person person1 = new Person(new Cat());person1.feedCat();}
}

这倒是满足了现在的需求,但如果现在要求person喂鸟,喂鱼,喂…等,难道每个都要新建类然后改动Person里面的函数?这显然不是好的设计.此时每当下游发生改动,上层代码都要更改,违反了依赖倒转原则.

此时正确的写法为:新建一个Animal接口,让Person依赖于Animal接口,下层的动物如猫狗等应该实现该Animal接口,此时原来的上下层都依赖该接口Animal.
只要实现该接口的类就可以传进去.

里氏替换原则

所有父类对象出现的位置都可以使用子类对象无条件替换,且要保证业务不受影响。
在继承中有两个限制:

  • 子类方法不允许比父类方法的访问限制更严格
  • 子类方法不允许比父类方法抛出更多的异常
    上述的限制其实就是在要求继承关系要满足里式替换原则。

关于继承,我们通常说他们需要满足is-a关系,但是里式替换要求我们不光要满足is-a关系,还要满足业务场景逻辑的正确。所有,正方形是长方形吗?不一定。
在下面的业务逻辑下,计算长方形的面积,此时Squre确实是Rectangle的子类,满足继承关系,因为他们的业务逻辑是一样的。

class Rectangle {private int length;private int width;public int area() {return length * width;}
}class Squre extends Rectangle {private int length;@Overridepublic int area() {return length * length;}
}

但下面的业务逻辑下就不成立了:

public class Main {public static void main(String[] args) {Rectangle rectangle = new Rectangle();rectangle.setWidth(12);rectangle.setLength(20);Util.op(rectangle);}
}class Rectangle {private int length;private int width;public int getLength() {return length;}public void setLength(int length) {this.length = length;}public int getWidth() {return width;}public void setWidth(int width) {this.width = width;}public int area() {return length * width;}
}class Squre extends Rectangle {private int length;@Overridepublic int getLength() {return length;}@Overridepublic void setLength(int length) {length = length;}@Overridepublic int getWidth() {return length;}@Overridepublic void setWidth(int width) {length = width;}@Overridepublic int area() {return length * length;}
}class Util {/*业务场景:不断累加宽,直到它比长多1为止*/public static void op(Rectangle rectangle) {while (rectangle.getWidth() <= rectangle.getLength()) {rectangle.setWidth(rectangle.getWidth() + 1);System.out.println(rectangle.getLength()+ "      "+ rectangle.getWidth());}}
}

在上面的情形下,如果将父类main函数(业务逻辑实现处)Rectangle对象替换为Squre对象,会照成代码的死循环,这显然是不符合里式替换原则的。所以在这种情况下,并不能简单的认为正方形是长方形的子类。

继承关系中,除了看是否有is-a关系外,更重要的是在替换的情况下,代码的业务逻辑是否准确无误。

开闭原则

对扩展开放,对修改关闭.开闭原则在七大原则中优先级很高,有时会为了符合该原则而牺牲其他原则.
加新功能的同时必须保证原有代码的稳定,保证原有功能的稳定.

如果全是自己的源代码,不允许修改原来的源代码,如果是导入的jar包,你甚至想改也改不到.

程序员分为两种,以某框架为例,可以分为框架的作者和框架的用户.但是这两个角色都应该严格符合开闭原则.

最少知道原则

  • 上层类对下层类的实现细节要知道的越少越好
    主要体现封装的思想。一个类应该尽量将自己的实现细节留在自己的内部,不要暴露太多细节给其他类。
    现在有一个Computer类,现在要模仿它关机的执行流程,反例代码如下:
public class Computer {public void saveData() {//保存数据System.out.println("saveData");}public void closeScreen() {//关闭屏幕System.out.println("closeScreen");}public void killProcess() {//杀死进程System.out.println("killProcess");}public void powerOff() {//关掉电源System.out.println("powerOff");}
}class Person{private Computer computer = new Computer();public void shutDownComputer() {//太多实现细节在这里暴露computer.saveData();computer.killProcess();computer.closeScreen();computer.powerOff();}
}

如上面的例子,Person对象只是想关掉电脑,他并不需要知道电脑关机的具体流程,这样的架构不但让代码冗余,而且可能会带来其他的问题。倘若客户并不清楚电脑关闭的操作步骤呢?可以直接关闭电源而没有保存数据呢?这都是暴露太多细节带来的问题。正确的做法应该是:

public class Computer {private void saveData() {System.out.println("saveData");}private void closeScreen() {System.out.println("closeScreen");}private void killProcess() {System.out.println("killProcess");}private void powerOff() {System.out.println("powerOff");}public void shutDown() {this.saveData();this.killProcess();this.closeScreen();this.powerOff();}
}class Person{private Computer computer = new Computer();public void shutDownComputer() {computer.shutDown();}
}

重构之后,电脑关机的细节全部定义在Computer类内部,保证了操作步骤的正确性,毕竟该类的作者才是最最懂关机细节的人。Computer对外暴露shutDown函数,Person只需调用该方法就完成整个关机流程。代码更整洁漂亮,而且不会出错。

  • 只和朋友进行通信
    朋友的定义:
    • 类中的字段类型
    • 类中的方法的参数类型
    • 类中的方法的返回值类型
    • 方法中直接实例化出来的类型(如使用new)
      总结一句话就是:该类依赖的所有类都是自己的朋友。
      那么,哪种类不算自己的朋友呢?
public class AppTest {private Foo foo;void f1() {Bee bee = foo.getBee();}
}class Foo{Bee getBee() {return new Bee();}
}class Bee{}

Bee类就不是AppTest类的朋友,不符合上述朋友的任何一条。我们应该尽量不在AppTest中调用Bee的方法。此时Bee对象可以作为参数。

只和朋友通信是理想情况,实际开发中要视自己的业务需求来做判断。可以适当违背这一条。

合成复用原则

尽量使用组合而不是继承。继承关系会将类紧紧耦合在一起,而且在某些业务场景下会导致类数目的爆炸性增长。

这篇关于设计模式概述以及七大设计原则的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/928585

相关文章

十五.各设计模式总结与对比

1.各设计模式总结与对比 1.1.课程目标 1、 简要分析GoF 23种设计模式和设计原则,做整体认知。 2、 剖析Spirng的编程思想,启发思维,为之后深入学习Spring做铺垫。 3、 了解各设计模式之间的关联,解决设计模式混淆的问题。 1.2.内容定位 1、 掌握设计模式的"道" ,而不只是"术" 2、 道可道非常道,滴水石穿非一日之功,做好长期修炼的准备。 3、 不要为了

在线装修管理系统的设计

管理员账户功能包括:系统首页,个人中心,管理员管理,装修队管理,用户管理,装修管理,基础数据管理,论坛管理 前台账户功能包括:系统首页,个人中心,公告信息,论坛,装修,装修队 开发系统:Windows 架构模式:B/S JDK版本:Java JDK1.8 开发工具:IDEA(推荐) 数据库版本: mysql5.7 数据库可视化工具: navicat 服务器:SpringBoot自带 ap

《计算机英语》Unit 1 Computer Overview 计算机概述

期末试卷组成 1、选择20道 2、判断20道 3、词汇翻译(单词+词组,参照课后习题) 4、翻译2道(一道原题,参照作业) SectionA About Computer 关于计算机 algorithm          n.  算法  operate          v.  操作  digital           adj. 数字的  integrated circuit

DDei在线设计器-API-DDeiSheet

DDeiSheet   DDeiSheet是代表一个页签,一个页签含有一个DDeiStage用于显示图形。   DDeiSheet实例包含了一个页签的所有数据,在获取后可以通过它访问其他内容。DDeiFile中的sheets属性记录了当前文件的页签列表。   一个DDeiFile实例至少包含一个DDeiSheet实例。   本篇最后提供的示例可以在DDei文档直接预览 属性 属性名说明数

基于Springboot + vue 的抗疫物质管理系统的设计与实现

目录 📚 前言 📑摘要 📑系统流程 📚 系统架构设计 📚 数据库设计 📚 系统功能的具体实现    💬 系统登录注册 系统登录 登录界面   用户添加  💬 抗疫列表展示模块     区域信息管理 添加物资详情 抗疫物资列表展示 抗疫物资申请 抗疫物资审核 ✒️ 源码实现 💖 源码获取 😁 联系方式 📚 前言 📑博客主页:

比较学习难度:Adobe Illustrator、Photoshop和新兴在线设计平台

从入门设计开始,几乎没有人不知道 Adobe 公司两大设计软件:Adobe Illustrator和 Photoshop。虽然AI和PS很有名,有一定设计经验的设计师可以在早期探索和使用后大致了解AI和PS的区别,但似乎很少有人会系统地比较AI和PS。目前,设计软件功能多样,轻量级和网页设计软件已成为许多设计师的需求。对于初学者来说,一篇有针对性的AI和PS比较总结文章具有非常重要的指导意义。毕竟

【云计算 复习】第1节 云计算概述和 GFS + chunk

一、云计算概述 1.云计算的商业模式 (1)软件即服务(SaaS) 有些景区给游客提供烧烤场地,游客需要自己挖坑或者砌烧烤台,然后买肉、串串、烧烤。 (2)平台即服务(PaaS) 有些景区给游客提供烧烤场地,同时搭建好烧烤台,游客只需要自己带食材和调料、串串、烧烤。 (3)基础设施即服务(IaaS) 有些景区给游客提供烧烤场地,同时搭建好烧烤台,还有专门的厨师来烧烤,用户不需要关心前面的所有

基于Java医院药品交易系统详细设计和实现(源码+LW+调试文档+讲解等)

💗博主介绍:✌全网粉丝10W+,CSDN作者、博客专家、全栈领域优质创作者,博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 🌟文末获取源码+数据库🌟 感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,希望帮助更多的人  Java精品实战案例《600套》 2023-2025年最值得选择的Java毕业设计选题大全:1000个热

展厅设计主要的六大要素

1、从创意开始      展示设计的开始必须创意在先。根据整体的风格思路进行创意,首先要考虑的是主体的造型、大小高度位置以及它和周围展厅的关系。另外其他道具设计制作与运作方式也必须在创意中有明确的体现。      2、平面感      平面感是指对展示艺术设计平面图纸审美和功能两个方面理性的感觉认识。它是三维空间设计认识的基础,也是施工的重要依据。展示空间的设计应先在展场环境的平面

从《深入设计模式》一书中学到的编程智慧

软件设计原则   优秀设计的特征   在开始学习实际的模式前,让我们来看看软件架构的设计过程,了解一下需要达成目标与需要尽量避免的陷阱。 代码复用 无论是开发何种软件产品,成本和时间都最重要的两个维度。较短的开发时间意味着可比竞争对手更早进入市场; 较低的开发成本意味着能够留出更多营销资金,因此能更广泛地覆盖潜在客户。 代码复用是减少开发成本时最常用的方式之一。其意图