设计模式 - 六大设计原则(1)

六大设计原则

1、单一职责原则

特点: 类和方法属性等,都应当遵守单一职责。尽可能保持统一性,单一性。
含义:
(1)统一性,定义一个模块就必须要符合所有对象的行为特征。比如声明一个 Animal 类,有飞、呼吸等行为。而如果实例化一个动物鱼,则鱼不符合飞的行为特征,也就不满足统一性。
(2)单一性。只做一种事情。比如声明一个文件下载上传的类。在类里面,不仅要实现文件的下载上传,还对文件进行读写的功能等。该类在这过程中做了多件不同的事情,不符合单一职责原则。

优点: 高内聚,修改当前模块对其他模块的影响很低。
缺点: 过度单一,过度细分,就会增加系统的复杂程度。

反例:

    public class Files{ // 违反单一性:该类处理三种不同的逻辑,分别为接受发送文件、读写文件、连接网络。//1.负责接受发送文件public void ReveFile() { }public void SendFile() { }//2.负责读写文件public void Write(){}public void Read(){}// 不具有统一性,如果是 PDF 文件,则不能通过 TXT 记事本来打开。public void OpenToTxt(string file) { }//3:处理网络的连接public void SocketFor(string socketType) {//在此方法内,实现了多种不同的逻辑,同样不符合单一职责原则/*//1).sokcet 绑定Socket.Binding();...//2). 检测网络问题if (CheckNet.IsNet) setNet else Failed Link.. //3).更新页面上的网络状态Task.Run(=> NetState.Refresh...)...*/}}

正例:

    //单一职责:通过 soket 来接受发送文件public class HttpsFile{Sockets socket;public void Reve() { }public void Send() { }}//单一职责:读写文件public class CustomFile{public void Write() { }public void Read() { }public void OpenToTxt(string file) { }}//单一职责:连接网络public class Sockets{public Sockets(string type) { }private void Bingding() { }private bool CheckNet() { return true; }private void RefreshState() { }}

2、开闭原则

特点: 把类、模块、函数等抽象化,对外扩展,对内不得修改。
含义:用伪代码理解-》

public interface IFile
{
//...不得修改接口内部的代码
}//所谓对外,就是对实现不同的类进行扩展。
//1.扩展 Word 类,Excel 类...
public class Word:IFile,IReader...
{
//2.扩展新功能 void setOutLine();...
}

优点: 新加功能不需要对已有的模块进行修改,而是对外扩展来实现。

反例:

    public class OpenFile{public void OpenToTXT(string fileName){Console.Write("Open txt 文件");}public void OpenToPDFReader(string fileName){Console.Write("Open pdf 文件");}//会潜在新需求//比如通过浏览器打开html文件、通过视频软件打开mp4等等//......//该类违反了开闭原则。一旦有新需求就会对该类进行修改代码。//这会影响其他引用该类的代码逻辑}

正例:

    //把打开文件模块抽象化public interface IOpenFile{void Open(IFileType fileType);}//把文件类型模块抽象化public interface IFileType{ String format { get; }}//修改特征public class PDFFormat : IFileType{public String format { get { return "PDF"; } }}//修改特征public class SQLFormat : IFileType{public String format { get { return "mdf"; } }}//修改特征public class OpenPDF : IOpenFile{public void Open(IFileType fileType){Console.WriteLine("Opened File is " + fileType.format);}}//扩展新功能public class OpenSQL : IOpenFile{public void Open(IFileType fileType){var bstate = linkSqlManager("123");if (bstate)Console.WriteLine("Opened File is " + fileType.format);}//新功能:因为查看SQL文件,需要先连接数据库private bool linkSqlManager(string pwd){string stateStr = pwd == "123" ? "连接成功" : "连接失败";bool result = pwd == "123" ? true : false;Console.WriteLine(stateStr);return result;}}class Program{static void Main(string[] args){var openPDF = new OpenPDF();openPDF.Open(new PDFFormat());var openSQL = new OpenSQL();openSQL.Open(new SQLFormat());}}

3、依赖倒置原则

特点: 依赖于抽象接口,不依赖于具体实现。
依赖性: A类依赖B类,当B类修改或消失时,对A类会影响很大。

符合依赖倒置原则:

  • A类作为主调用者,不应该依赖被调用者B类。A类和B类应当都依赖于抽象。即A类依赖于IA接口,B类依赖于IA接口。

**优点:**降低了模块之间的耦合性。

反例:
假如有制造交通工具的工厂,根据车的型号来生产一辆车。

public class Factory{//Factory 类依赖 Vehicle 类,//当 Vehicle 内部被修改时或者消失掉,将会影响到 Factory 类的 make 方法public void make(Vehicle v) {Console.WriteLine(v.model + v.color);}}//如果工厂需要同时制造一辆自行车和汽车时,则生产的需求就不同了。
//在这样的情况下,会修改Vehicle 类,会可能影响这两种车的制造。
//比如汽车需要设计安全带,自行车需要车篮。public class Vehicle{public string model { get; private set; }public string color { get; private set; }public Vehicle(string model, string color){this.model = model; this.color = color;}}class Program
{
static void Main(string args)
{Vehicle v = new Vehicle("奥迪","黑色");Factory factory = new Factory();factory.make(v);
}
}

正例:

(1)定义设计交通工具的接口:

//设计交通工具抽象化,作为父接口public interface IVehicle{string Model { get; }//车类型string AppearColor { get; }//外观颜色string Design();//设计工作}//设计自行车抽象化,继承 IVehiclepublic interface IBicycle : IVehicle{string Basket { get; }//车篮}//设计汽车抽象化,继承 IVehiclepublic interface ICar : IVehicle{string Safetybelt { get;}//安全带}

(2)实现交通工具的接口:

    public class Car: ICar{private string _model;public string Model { get { return _model; } }private string _safetybelt;public string Safetybelt { get { return _safetybelt; } }public Car(sstring model, string safetybelt){_model = model;_safetybelt = safetybelt;}public string Design(){return Model + Safetybelt;}}public class Bicycle : IBicycle{private string _model;public string Model { get { return _model; } }private string _basket;public string Basket { get { return _basket; } }public Bicycle(string model, string basket){_model = model;_basket = basket;}public string Design(){return  Model + Basket;}}

(3)定义工厂的接口:

   public interface IFactory{string who();}

(4)实现工厂的接口:

    public class CarFactory : IFactory{public string who(){return "汽车制造商";}}public class BicycleFactory : IFactory{public string who(){return "自行车制造商";}}

(5)中间类用来处理 IVehicle 接口(父接口) 和 IFactory 接口:

public class Maker{//由于传入参数为原始(父接口)的接口类型,不管外面传入的实现类是什么,将来会修改成什么样子//只要实现类依赖于这些接口,就不会影响到该类执行的代码逻辑。public void Work(IFactory factory, IVehicle vehicle){Console.WriteLine(factory.who() + vehicle.Design());}}class Program{static void Main(string[] args){Maker maker = new Maker();CarFactory carFactory = new CarFactory();Car car = new Car("奥迪", "三点式安全带");maker.Work(carFactory, car);BicycleFactory bicycleFactory = new BicycleFactory();Bicycle bicycle = new Bicycle("欧拜克","灰色车篮");maker.Work(bicycleFactory, bicycle);//工厂类和交通工具类在各自内部之间不存在互相依赖,不存在调用和被调用的关系。//这样就降低了模块之间的耦合性。//maker 作为中间类,Work 方法也只是依赖了接口,而并非实现类。}}

4、里氏替换原则

特点: 定义一个父类,其子类就必须完全继承父类的所有特征。这样父类替换成子类后,也不会有任何变化。

反例:

    public abstract class Vehicle{public abstract void Driver();}public class Car : Vehicle{public override void Driver(){}}//把 父类 Vehicle 可以替换成 Car 类来使用,完全不会影响。因为 Car 继承了 Driver 方法。
//但是如果自行车 Bicycle 继承于 Vehicle,问题就来了。public class Bicycle: Vehicle{//继承 Driver 方法显然是不合理的,自行车是骑的,而不是开的public override void Driver() { }}// 也就是说,Bicycle 类没有完全可以实现 Vehicle 里的所有方法属性等。
//所以违反了里氏替换原则

正例:

public abstract class Vehicle{public abstract void Move();}public class Car: Vehicle{public override void Move() { Console.Write("Driver");}}public class Bicycle: Vehicle{public override void Move() { Console.Write("By Bike");}}

5、接口隔离原则

特点:定义一个接口时,不能定义一些不需要的方法属性等。应当要建立在最小的接口上。(接口里的所有特征应当都要被用于实现类上)

反例:

public interface IAnimal{void Eat();void Talk();void Look();void Fly();void Run();void Play();//还有其他行为......}public class Cat: IAnimal{public void Eat() { Console.WriteLine("吃鱼"); }public void Talk() { Console.WriteLine("喵!"); }public void Look() { Console.WriteLine("眨眼"); }public void Fly() { Console.WriteLine("..."); }public void Run() { Console.WriteLine("跑"); }public void Play() { Console.WriteLine("啃纸皮"); }//还有其他行为......//问题1:猫不会飞,自然不要有这样的一种行为出现。即Fly 方法不应该出现。}class Program{static void Main(string[] args){//问题2:Cat 依赖 IAnimal接口后,只看到了猫在玩耍的一种行为。//而其他那些行为,可以说是基本上没什么用了。Cat cat = new Cat();cat.Play();}}

反例2:


//如果把 Animal 接口进一步拆分成:public interface IMouth{void Eat();void Talk();}public interface IEar{void Look();}public interface Iwing{void Fly();void Swim();}public interface IFoot{void Run();void walk();void Climb();}//同样也是违反了接口隔离原则,因为比如:
//Iwing 有 飞和游泳的特征。鱼儿可以用翅膀游泳,小鸟可以扇动翅膀飞翔。
//但是鱼儿不能飞,一些小鸟不能游泳。//显然不满足这个条件:
//接口里的所有特征应当都要被用于实现类上

正例:

//拆分成最小的接口
public interface IEat{void Eat();}public interface IPlay{void Play();}public class Cat:IEat, IPlay{public void Eat() { Console.WriteLine("猫在吃鱼干"); }public void Play() { Console.WriteLine("猫在啃纸皮"); }}//解决1:Eat 和 Play 是猫的行为特征,都是需要的。
//解决2:只定义接口 IEat 和 IPlay,是最小的接口,节省了很多不必要的行为。
//如果有新的需求,猫要喵喵叫,就直接再定义一个“叫”行为的接口。

6、迪米特法则(最少知识原则)

原则1: 减少模块与模块之间的依赖性。比如模块 A 和模块 B 互相依赖,模块C和B互相依赖,而 C 跟 A 没有关系。C 里没有 A的存在,反之亦然。

原则2: 降低模块与模块之间的耦合性。比如 A 被 B 调用,A 类存在于 B 类里。但是 A 的部分成员都设置为私有,不能访问 A 类 的私有成员, B 类只能访问 A类的公有成员。
(public、private 等访问机制,要根据情况合理设定,不要滥用)

优点: 减少模块间的依赖,降低模块间的耦合性。提高代码的复用率。

反例:
根据以上ABC类描述的,数据分析师类作为 C 类,数据管理员类作为 B 类,顾客类作为 A 类。

假定以下代码没有遵循原则1 和原则2:

  • 违反原则1:C 类能够直接对A类操作,A存在于C类。
  • 违反原则2:A 类的一些pwd、isVIP等重要成员以 public 对外开放,导致外部类可以轻易修改这些成员的逻辑。
//顾客public class Customer{public string pwd;public bool isVIP;public string name { get; private set; }}//数据分析师public class DataAnalyst{// DataAnalyst 和 Customer 存在依赖关系。//如果 Customer 逻辑被修改,则会影响到 DataAnalyst。public void GetCustomersData(DataManager manager){var customers = manager.GetCustomers();var count = customers.Count;//如果 isVIP 字段访问改成 private,则会报错。var vip = customers[0].isVIP;}}//数据管理员public class DataManager{private List<Customer> Customers;public List<Customer> GetCustomers() { return Customers; }}

正例:

//顾客//私有变量限制 DataManager 对 Customer的操作范围public class Customer{private string pwd;private bool _isVIP;private bool isVIP { get { return _isVIP; } }public string name { get; private set; }public void setVip(bool isvip){_isVIP = isvip;}}//数据分析师//只需要通过 DataManager 间接获取 Customer 的一些信息。//保证了 DataAnalyst 和 Customer 不存在依赖关系。//Customer 即便修改了代码逻辑,也影响不了 DataAnalyst。public class DataAnalyst{public void GetCustomersData(DataManager manager){var count = manager.GetCustomerCount();}}//数据管理员public class DataManager{private List<Customer> Customers;private void setVip(int index) { Customers[index].setVip(true); }public int GetCustomerCount() { return Customers.Count; }}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/147552.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【每日一题】—— C. Yarik and Array(Codeforces Round 909 (Div. 3))(贪心)

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;每日一题 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日反刍 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓称…

【STM32】RTC(实时时钟)

1.RTC简介 本质&#xff1a;计数器 RTC中断是外部中断&#xff08;EXTI&#xff09; 当VDD掉电的时候&#xff0c;Vbat可以通过电源--->实时计时 STM32的RTC外设&#xff08;Real Time Clock&#xff09;&#xff0c;实质是一个 掉电 后还继续运行的定时器。从定时器的角度…

科技创新 共铸典范 | 江西卫健办邓敏、飞图影像董事长洪诗诗一行到访拓世科技集团,提振公共卫生事业发展

2023年11月15日&#xff0c;拓世科技集团总部迎来了江西省卫健项目办项目负责人邓敏、江西飞图影像科技有限公司董事长洪诗诗一行的考察参观&#xff0c;集团董事长李火亮、集团高级副总裁方高强进行热情接待。此次多方交流&#xff0c;旨在共同探讨携手合作&#xff0c;激发科…

2023年11月12日阿里云产品全面故障的启示

2023年11月12日&#xff0c;阿里云产品因为某些故障&#xff0c;全线都受到影响。是的&#xff0c;双十一的第二天&#xff0c;我的购物车还没清空&#xff0c;阿里云就不让我买了。云产品全面故障&#xff0c;影响之大一个大铁锅都装不下。之所以阿里云故障受到大家这么关注&a…

ClickHouse建表优化

1. 数据类型 1.1 时间字段的类型 建表时能用数值型或日期时间型表示的字段就不要用字符串&#xff0c;全String类型在以Hive为中心的数仓建设中常见&#xff0c;但ClickHouse环境不应受此影响。 虽然ClickHouse底层将DateTime存储为时间戳Long类型&#xff0c;但不建议存储Long…

【Java】ArrayList和LinkedList使用不当,性能差距会如此之大!

文章目录 前言源码分析ArrayList基本属性初始化新增元素删除元素遍历元素 LinkedList实现类基本属性节点查询新增元素删除元素遍历元素 分析测试 前言 在面试的时候&#xff0c;经常会被问到几个问题&#xff1a; ArrayList和LinkedList的区别&#xff0c;相信大部分朋友都能回…

自动化网络图软件

由于 IT 系统的发展、最近向混合劳动力的转变、不断变化的客户需求以及其他原因&#xff0c;网络监控变得更加复杂。IT 管理员需要毫不费力地可视化整个网络基础设施&#xff0c;通过获得对网络的可见性&#xff0c;可以轻松发现模式、主动排除故障、确保关键设备可用性等。 为…

SEnet注意力机制(逐行代码注释讲解)

目录 ⒈结构图 ⒉机制流程讲解 ⒊源码&#xff08;pytorch框架实现&#xff09;及逐行解释 ⒋测试结果 ⒈结构图 左边是我自绘的&#xff0c;右下角是官方论文的。 ⒉机制流程讲解 通道注意力机制的思想是&#xff0c;对于输入进来的特征层&#xff0c;我们在每一个通道学…

阿里云+宝塔部署项目(Java+React)

阿里云服务器宝塔面板部署项目&#xff08;SpringBoot React&#xff09; 1. 上传所需的文件到服务器 比如jdk包和java项目的jar&#xff1a;这里以上传jar 为例&#xff0c;创建文件夹&#xff0c;上传文件&#xff1b; 在创建的文件夹下上传jar包 上传jdk 2. 配置jdk环境 3.…

OpenAI 解雇了首席执行官 Sam Altman

Sam Altman 已被 OpenAI 解雇&#xff0c;原因是担心他与董事会的沟通和透明度&#xff0c;可能会影响公司的发展。该公司首席技术官 Mira Murati 将担任临时首席执行官&#xff0c;但 OpenAI 可能会从科技行业寻找新的首席执行官来领导未来的产品开发。Altman 的解雇给 OpenAI…

实验(二):存储器实验

一、实验内容与目的 实验要求&#xff1a; 利用 CP226 实验仪上的 K16..K23 开关做为 DBUS 的数据&#xff0c;其它开关做为控制信号&#xff0c;实现主存储器 EM 的读写操作&#xff1b;利用 CP226 实验仪上的小键盘将程序输入主存储器 EM&#xff0c;实现程序的自动运行。 实…

Gem5模拟器学习之旅——翻译自官网

文章目录 安装并使用gem5 模拟器支持的操作系统和环境依赖在 Ubuntu 22.04 启动(gem5 > v21.1)Docker获取代码用 SCons 构建用法首次构建 gem5gem5 二进制类型调试opt快速 常见错误错误的 gcc 版本Python 位于非默认位置未安装 M4 宏处理器Protobuf 3.12.3 问题 安装并使用g…

如何零基础自学AI人工智能

随着人工智能&#xff08;AI&#xff09;的快速发展&#xff0c;越来越多的有志之士被其强大的潜力所吸引&#xff0c;希望投身其中。然而&#xff0c;对于许多零基础的人来说&#xff0c;如何入门AI成了一个难题。本文将为你提供一份详尽的自学AI人工智能的攻略&#xff0c;帮…

如何理解IOC中的反射操作

控制反转&#xff08;Inversion of Control&#xff0c;IoC&#xff09;是一种设计模式&#xff0c;其中控制流程的一部分由框架负责&#xff0c;而不是由应用程序代码直接控制。在IoC容器中&#xff0c;反射&#xff08;Reflection&#xff09;是一种机制&#xff0c;用于在运…

Idea 创建 Spring 项目(保姆级)

描述信息 最近卷起来&#xff0c;系统学习Spring&#xff1b;俗话说&#xff1a;万事开头难&#xff1b;创建一个Spring项目在网上找了好久没有找到好的方式&#xff1b;摸索了半天产出如下文档。 在 Idea 中新建项目 填写信息如下 生成项目目录结构 pom添加依赖 <depende…

ubuntu18.04中代码迁移到20.04报错

一、 PCL库&#xff0c;Eigen库报错&#xff0c;如&#xff1a; /usr/include/pcl-1.10/pcl/point_types.h:903:29: error: ‘enable_if_t’ in namespace ‘std’ does not name a template type; did you mean ‘enable_if’?/usr/include/pcl-1.10/pcl/point_types.h:698:…

修改 jar 包中的源码方式

在我们开发的过程中&#xff0c;我们有时候想要修改jar中的代码&#xff0c;方便我们调试或或者作为生产代码打包上线&#xff0c;但是在IDEA中&#xff0c;jar包中的文件都是read-only&#xff08;只读模式&#xff09;。那如何我们才能去修改jar包中的源码呢&#xff1f; 1.…

C语言编程陷阱(七)

陷阱31:不要使用数组名作为函数参数 C语言中,我们可以使用数组来存储多个相同类型的数据,数组有一个名字,一个类型,一个长度,和一个元素列表,比如int arr[5] = {1, 2, 3, 4, 5};表示一个数组,它的名字是arr,它的类型是int,它的长度是5,它的元素列表是{1, 2, 3, 4, 5…

Zabbix Proxy分布式监控

目录 Zabbix Proxy简介 实验环境 proxy端配置 1.安装仓库 2.安装zabbix-proxy 3.创建初始数据库 4.导入初始架构和数据&#xff0c;系统将提示您输入新创建的密码 5.编辑配置文件 /etc/zabbix/zabbix_proxy.conf&#xff0c;配置完成后要重启。 agent客户端配置 zabbix…

PostgreSQL 难搞的事系列 --- vacuum 的由来与PG16的命令的改进 (1)

开头还是介绍一下群&#xff0c;如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, Oceanbase, Sql Server等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友…