DesignPatternPrinciple-设计模式原则
1. 单一职责原则(Single Responsibility Principle)类T负责两个不同的职责:职责P1,职责P2。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DesignPatternPrinciple.SRP { /// <summary> /// 单一职责原则:类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时, /// 有可能会导致原本运行正常的职责P2功能发生故障。 /// /// 一个类只负责一件事儿 /// 面向对象语言开发,类是一个基本单位,单一职责原则就是封装的粒度 /// /// 写分支判断,然后执行不同的逻辑,其实这就违背了单一职责原则,但是功能是可以实现的 /// /// 拆分父类+子类, /// 每个类很简单,简单意味着稳定 意味着强大 /// (现在的东西没有以前经用,因为功能多了,这不坏那坏了) /// /// 拆分后,也会造成代码量的增加, /// 类多了,使用成本也高(理解成本) /// /// 究竟该什么时候使用单一职责原则呢? /// 如果类型足够简单,方法够少,是可以在类级别去违背单一职责 /// 如果类型复杂了,方法多了,建议遵循单一职责原则 /// /// 方法级别的单一职责原则:一个方法只负责一件事儿(职责分拆小方法,分支逻辑分拆) /// 类级别的单一职责原则:一个类只负责一件事儿 /// 类库级别的单一职责原则:一个类库应该职责清晰 /// 项目级别的单一职责原则:一个项目应该职责清晰(客户端/管理后台/后台服务/定时任务/分布式引擎) /// 系统级别的单一职责原则:为通用功能拆分系统(IP定位/日志/在线统计) /// /// </summary> public class SRPShow { public static void Show() { { Animal animal = new Animal("鸡");//呼吸空气 animal.Breath(); //animal.Action(); } { Animal animal = new Animal("牛");//呼吸空气 animal.Breath(); //animal.Action(); } { Animal animal = new Animal("鱼");//呼吸水 animal.Breath(); animal.Action(); } { AbstractAnimal animal = new Chicken(); animal.Breath(); animal.Action(); } { AbstractAnimal animal = new Fish(); animal.Breath(); animal.Action(); } } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DesignPatternPrinciple.SRP { /// <summary> /// 封装 /// 动物类 /// 简单意味着稳定 /// </summary> public class Animal { private string _Name = null; public Animal(string name) { this._Name = name; } /// <summary> /// 这个方法就挺不稳定,经常各种分支变化经常修改 /// </summary> public void Breath() { if (this._Name.Equals("鸡")) Console.WriteLine($"{this._Name} 呼吸空气"); else if (this._Name.Equals("牛")) Console.WriteLine($"{this._Name} 呼吸空气"); else if (this._Name.Equals("鱼")) Console.WriteLine($"{this._Name} 呼吸水"); else if (this._Name.Equals("蚯蚓")) Console.WriteLine($"{this._Name} 呼吸泥土"); } //BreathChicken BreathFish //应该拆分了 public void Action() { if (this._Name.Equals("鸡")) Console.WriteLine($"{this._Name} flying"); else if (this._Name.Equals("牛")) Console.WriteLine($"{this._Name} walking"); else if (this._Name.Equals("鱼")) Console.WriteLine($"{this._Name} Swimming"); else if (this._Name.Equals("蚯蚓")) Console.WriteLine($"{this._Name} Crawling"); } } }
2. 里氏替换原则(Liskov Substitution Principle):任何使用基类的地方,都可以透明的使用其子类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DesignPatternPrinciple.LSP { /// <summary> /// 里氏替换原则:任何使用基类的地方,都可以透明的使用其子类 /// 继承、多态 /// 继承:子类拥有父类的一切属性和行为,任何父类出现的地方,都可以用子类来代替 /// 继承+透明(安全,不会出现行为不一致) /// /// 1 父类有的,子类是必须有的; /// 如果出现了子类没有的东西,那么就应该断掉继承; /// (再来一个父类,只包含都有的东西) /// 2 子类可以有自己的属性和行为 /// 子类出现的地方,父类不一定能代替 /// 白马非马 /// 3 父类实现的东西,子类就不要再写了,(就是不要new隐藏) /// 有时候会出现意想不到的情况,把父类换成子类后,行为不一致 /// 如果想修改父类的行为,通过abstract/virtual /// /// 声明属性、字段、变量,尽量声明为父类(父类就能满足) /// /// </summary> public class LSPShow { public static void Show() { Console.WriteLine("***************************"); Poly.Test(); { //People people = new Chinese(); Chinese people = new Chinese(); people.Traditional(); //DoChinese(people); people.SayHi(); } { Chinese people = new Hubei(); people.Traditional(); //DoChinese(people); people.SayHi(); } { var people = new Hubei(); people.Traditional(); //DoChinese(people); people.SayHi(); } { People people = new Japanese(); people.Traditional(); //要么上端调用去判断 不能写 因为Japanese没有Traditional //要么就只能在子类抛异常 } { Hubei people = new Hubei(); people.Traditional(); DoChinese(people); DoHubei(people); } { //People people = new People(); //Do(people); } } private static void DoChinese(Chinese people) { Console.WriteLine($"{people.Id} {people.Name} {people.Kuaizi}"); people.SayHi(); } private static void DoHubei(Hubei people) { Console.WriteLine($"{people.Id} {people.Name} {people.Kuaizi}"); people.SayHi(); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DesignPatternPrinciple.LSP { public class People { public int Id { get; set; } public string Name { get; set; } //abstract void Eat(); public void Traditional() { Console.WriteLine("仁义礼智信 温良恭俭让 "); } } public class Chinese : People { public string Kuaizi { get; set; } public void SayHi() { Console.WriteLine("早上好,吃了吗?"); } } public class Hubei : Chinese { public string Majiang { get; set; } public new void SayHi() { Console.WriteLine("早上好,过早了么?"); } } //public class Animal//让People继承自Animal //{ // public int Id { get; set; } // public string Name { get; set; } //} public class Japanese : People { //public int Id { get; set; } //public string Name { get; set; } //public new void Traditional() //{ // Console.WriteLine("忍者精神 "); //throw new Exception(); //} //Traditional也会继承 但是Japanese又没有Traditional public void Ninja() { Console.WriteLine("忍者精神 "); } } }
3. 依赖倒置原则(Dependence Inversion Principle)依赖倒置原则:上层模块不应该依赖于低层模块,二者应该通过抽象依赖
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DesignPatternPrinciple.DIP { /// <summary> /// 依赖倒置原则:高层模块不应该依赖于低层模块,二者应该通过抽象依赖 /// 依赖抽象,而不是依赖细节 /// 抽象:接口/抽象类--可以包含没有实现的元素 /// 细节:普通类--一切都是确定的 /// /// 面向抽象编程:尽量的使用抽象,80%的设计模式都是跟抽象有关 /// 属性 字段 方法参数 返回值。。。尽量都是抽象 /// /// /// </summary> public class DIPShow { public static void Show() { Console.WriteLine("**************DIPShow***************"); Student student = new Student() { Id = 191, Name = "候鸟班长" }; //Student-使用-手机 //高层---------底层 { iPhone phone = new iPhone(); student.PlayiPhone(phone); student.PlayT(phone); student.Play(phone); } { Lumia phone = new Lumia(); student.PlayLumia(phone); student.PlayT(phone); student.Play(phone); } { Honor phone = new Honor(); student.PlayHonor(phone); student.PlayT(phone); student.Play(phone); } //依赖细节 高层就依赖了底层 //手机扩展一下,学生就得改一下。。。手机多了怎么办 { Mi phone = new Mi(); student.PlayT(phone); student.Play(phone); } { Oppo phone = new Oppo(); student.PlayT(phone); student.Play(phone); } //用泛型+父类约束其实就等同于用父类参数类型 //面向抽象有啥好处? //1 一个方法满足不同类型的参数, //2 还支持扩展,只要是实现了这个抽象,不用修改Student { //面向抽象后,不能使用子类的特别内容 Mi phone = new Mi(); student.Play(phone); //如果传递的是Mi,Bracelet是有的,但是方法确实不能用 //编译器决定了是不能用Bracelet的(dynamic/反射是可以调用的) //不能常规调用,这个问题是解决不了的, //因为面向抽象不止一个类型,用的就是通用功能;非通用的,那就不应该面向抽象 } //面向抽象,只要抽象不变,高层就不变 //面向对象语言开发,就是类与类之间进行交互,如果高层直接依赖低层的细节,细节是多变的,那么低层的变化就导致上层的变化;如果层数多了,底层的修改会直接水波效应传递到最上层,一点细微的改动都会导致整个系统从下往上的修改(这就是大家经常加班的原因) //面向抽象,如果高层和低层没有直接依赖,而是依赖于抽象,抽象一般是稳定的,那低层细节的变化扩展就不会影响到高层,这样就能支持层内部的横向扩展,不会影响其他地方,这样的程序架构就是稳定的 //依赖倒置原则(理论基础)---IOC控制反转(实践封装)---DI依赖注入(实现IOC的手段) } } }
4. 接口隔离原则(Interface Segregation Principle):客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上;
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DesignPatternPrinciple.ISP { /// <summary> /// 接口隔离原则:客户端不应该依赖它不需要的接口; /// 一个类对另一个类的依赖应该建立在最小的接口上; /// </summary> public class ISPShow { public static void Show() { Console.WriteLine("***************ISPShow*************"); Student student = new Student() { Id = 191, Name = "候鸟班长" }; //AbstractPhone定义了id branch call text is a //现有智能手机map movie online game.. //是否该把这几个上升到AbstractPhone? //不应该的,上升后,oldman也是手机,但是没有这些功能! //AbstractPhone就只能放入任何手机都必须具备的功能 { OldMan phone = new OldMan(); phone.Call(); phone.Text(); } //不适合放在抽象类,但是面向抽象编程,接口! //接口interface定义 can do 不局限产品 //Camera能拍照,能录像 //既然面向抽象,那么有这些功能的对象都得能传递进来 //那就让Camera也去实现IExtend { Honor honor = new Honor(); student.Happy(honor); student.Video(honor); } { Camera camera = new Camera(); student.Video(camera); } //实现IExtend接口,Camera出现很多自己没有的功能, //不应该用这种大而全的接口 { IExtendVideo camera = new Camera(); student.Video(camera); } { IExtendHappy extend = new TV(); student.Happy(extend); } { IExtendGame extend = new PSP(); student.Happy(extend); } //拆下去,都拆成一个方法一个接口,肯定也不好! { //List<> //Dictionary //IList<T> 索引相关 //ICollection<T> 集合相关操作 //IEnumerable<T> 迭代器foreach } //接口到底该如何定义? //1 既不能是大而全,会强迫实现没有的东西,也会依赖自己不需要的东西 //2 也不能一个方法一个接口,这样面向抽象也没有意义的 //按照功能的密不可分来定义接口, //而且应该是动态的,随着业务发展会有变化的,但是在设计的时候,要留好提前量,避免抽象的变化 //没有标准答案,随着业务和产品来调整的 //3 接口合并 Map--定位/搜索/导航 这种属于固定步骤,业务细节,尽量的内聚,在接口也不要暴露太多业务细节 } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DesignPatternPrinciple.ISP { public class Honor : AbstractPhone, IExtend, IExtendVideo, IExtendHappy { public override void Call() { Console.WriteLine("User {0} Call", this.GetType().Name); } public override void Text() { Console.WriteLine("User {0} Call", this.GetType().Name); } public void Photo() { Console.WriteLine("User {0} Photo", this.GetType().Name); } public void Online() { Console.WriteLine("User {0} Online", this.GetType().Name); } public void Game() { Console.WriteLine("User {0} Game", this.GetType().Name); } public void Map() { Console.WriteLine("User {0} Map", this.GetType().Name); } public void Pay() { Console.WriteLine("User {0} Pay", this.GetType().Name); } public void Record()//录音 { Console.WriteLine("User {0} Record", this.GetType().Name); } public void Movie() { Console.WriteLine("User {0} Movie", this.GetType().Name); } } }
5. 迪米特法则 (Law Of Demeter):一个对象应该对其他对象保持最少的了解。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DesignPatternPrinciple.LOD { /// <summary> /// 迪米特法则(最少知道原则):一个对象应该对其他对象保持最少的了解。 /// 只与直接的朋友通信。 /// 面向对象语言---万物皆对象---类和类交互才能产生功能--这不就耦合了吗? /// /// 类与类之间的关系: /// 纵向:继承≈实现(最密切) /// 横向:聚合> 组合> 关联> 依赖(出现在方法内部) /// /// 高内聚低耦合 /// 迪米特法则,降低类与类之间的耦合 /// 只与直接的朋友通信,就是要尽量避免依赖更多类型 /// 基类库(BCL--框架内置的)的类型除外 /// /// 迪米特,也会增加一些成本 /// /// 工作中有时候会去造一个中介/中间层 /// 门面模式 中介者模式 分层封装 /// 上层UI下订单---订单系统&支付系统&仓储&物流 /// 门面模式--上层交互门面--门面依赖子系统 /// 三层架构:UI---BLL---DAL /// /// 去掉内部依赖 /// 降低访问修饰符权限 /// private /// protected /// internal /// protected internal 子类或者同类库 /// public /// /// 迪米特,依赖别人更少,让别人了解更少 /// </summary> public class LODShow { public static void Show() { Console.WriteLine("************************"); School school = new School() { SchoolName = "SchoolName", ClassList = new List<Class>() { new Class() { ClassName="CN", StudentList=new List<Student>() { new Student() { StudentName="Tony" }, new Student() { StudentName="sada" } } } } }; school.Manage(); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DesignPatternPrinciple.LOD { /// <summary> /// 学生 /// </summary> public class Student { public int Id { get; set; } public string StudentName { get; set; } public int Height { private get; set; } public int Salay; public void ManageStudent() { Console.WriteLine(" {0}Manage {1} ", this.GetType().Name, this.StudentName); } } }
6: 开闭原则 (Open Closed Principle):对扩展开发,对修改关闭
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DesignPatternPrinciple.OCP { /// <summary> /// 开闭原则:对扩展开发,对修改关闭 /// 修改:修改现有代码(类) /// 扩展:增加代码(类) /// 面向对象语言是一种静态语言,最害怕变化,会波及很多东西 全面测试 /// 最理想就是新增类,对原有代码没有改动,原有的代码才是可信的 /// /// 开闭原则只是一个目标,并没有任何的手段,也被称之为总则 /// 其他5个原则的建议,就是为了更好的做到OCP /// 开闭原则也是面向对象语言开发一个终极目标 /// /// 如果有功能增加/修改的需求: /// 修改现有方法---增加方法---增加类---增加/替换类库 /// </summary> public class OCPShow { public static void Show() { Console.WriteLine("*************OCPShow***********"); } } }