设计模式:面向对象语言开发过程当中,遇到种种的场景和问题,提出的解决方案和思路,沉淀下来,设计模式是解决具体问题的套路编程
设计模式六大原则:面向对象语言开发过程当中,推荐的一些指导性原则,这些是没有明确的招数的,并且也常常被忽视或者违背!设计模式
一:单一职责原则(Single Responsibility Principle)安全
单一职责原则就是一个类只负责一件事儿,面向对象语言开发,类就是一个最基本的单位,单一职责的原则就是封装的粒度,主要关注的单个类实现的功能架构
好比咱们下面的例子框架
1 /// <summary> 2 /// 封装 3 /// 动物类 4 /// 简单意味着稳定 5 /// </summary> 6 public class Animal 7 { 8 private string _Name = null; 9 public Animal(string name) 10 { 11 this._Name = name; 12 } 13 14 //应该拆分了 15 public void Action() 16 { 17 if (this._Name.Equals("鸡")) 18 Console.WriteLine($"{this._Name} flying"); 19 else if (this._Name.Equals("牛")) 20 Console.WriteLine($"{this._Name} walking"); 21 else if (this._Name.Equals("鱼")) 22 Console.WriteLine($"{this._Name} Swimming"); 23 else if (this._Name.Equals("蚯蚓")) 24 Console.WriteLine($"{this._Name} Crawling"); 25 } 26 }
咱们声明一个动物,可是每一个动物的action是不同的,顺着咱们的思惟模式,咱们会在action中增长对应的if来判断,不一样的动物有不一样的动做,iphone
相似于这样的,在一个方法中写分支判断,而后执行不一样的逻辑,这就违背了单一职责原则,可是其实咱们想要的功能彻底能实现,若是种类比较少且不变的状况下,咱们彻底能够这样操做,可是若是种类比较多且常常容易发生改变,那咱们这样写就有很大的隐患,由于其中的改变有可能会影响到其它的。分布式
咱们能够对其进行改变,好比咱们能够先建立一个基类ide
1 public abstract class AbstractAnimal 2 { 3 protected string _Name = null; 4 public AbstractAnimal(string name) 5 { 6 this._Name = name; 7 } 8 9 public abstract void Breath(); 10 public abstract void Action(); 11 }
而后能够建立不一样动物的类来继承于这个基类this
public class Fish : AbstractAnimal { public Fish() : base("鱼") { } public override void Breath() { Console.WriteLine($"{base._Name} 呼吸水"); } public override void Action() { Console.WriteLine($"{base._Name} swimming"); } }
public class Chicken : AbstractAnimal { public Chicken() : base("鸡") { } public override void Breath() { Console.WriteLine($"{base._Name} 呼吸空气"); } public override void Action() { Console.WriteLine($"{base._Name} flying"); } }
相似于这样的,而后在本身的类中实现本身的方法,一个类只负责本身的事情,且都比较单一,简单意味着稳定,意味着强大,这就是所谓的单一职责原则,那么究竟何时回使用单一职责原则呢?若是类型复杂,方法多,这样建议使用单一职责原则!spa
那么使用单一职责原则也有本身的弊端,具体分为如下两个方面
1:代码量的增长(拆分开类的代码明显比以前增长)
2:使用成本就是所谓的理解成本增高(调用者要晓得不一样的类)
具体的单一原则分为如下五种
1:方法级别的单一职责原则:一个方法只负责一件事儿(职责分拆小方法,分支逻辑分拆)
2:类级别的单一职责原则:一个类只负责一件事儿
3:类库级别的单一职责原则:一个类库应该职责清晰
4:项目级别的单一职责原则:一个项目应该职责清晰(客户端/管理后台/后台服务/定时任务/分布式引擎)
5:系统级别的单一职责原则:为通用功能拆分系统(IP定位/日志/在线统计)
二: 里氏替换原则(Liskov Substitution Principle)
任何使用基类的地方,均可以透明的使用其子类,这主要是指 继承+透明(安全,不会出现行为不一致)
继承:子类拥有父类的一切属性和行为,任何父类出现的地方,均可以用子类来代替,主要是由于:
1:父类有的,子类是必须有的(私有不继承);若是出现了子类没有的东西,那么就应该断掉继承;、
2:子类能够有本身的属性和行为,可是子类出现的地方,父类不必定能代替
3:父类实现的东西,子类就不要再写了,(就是不要new隐藏),若是想修改父类的行为,经过abstract/virtual
举个例子:
1 public class People 2 { 3 public int Id { get; set; } 4 public string Name { get; set; } 5 7 public void Traditional() 8 { 9 Console.WriteLine("仁义礼智信 温良恭俭让 "); 10 } 11 } 12 13 public class Chinese : People 14 { 15 public string Kuaizi { get; set; } 16 public void SayHi() 17 { 18 Console.WriteLine("早上好,吃了吗?"); 19 } 20 21 } 22 23 public class Hubei : Chinese 24 { 25 public string Majiang { get; set; } 26 public new void SayHi() 27 { 28 Console.WriteLine("早上好,过早了么?"); 29 } 30 }
调用的时候:
{ Chinese people = new Chinese(); people.Traditional(); people.SayHi(); } { Chinese people = new Hubei(); people.Traditional(); people.SayHi(); } { var people = new Hubei(); people.Traditional(); people.SayHi(); }
上面须要注意的是:若是是普通的方法,以左边为主,就是左边是什么类,就调用谁的普通方法(编译时决定),若是是abstract或者virtual则是以右边为主(运行时决定),因此:父类有的方法,子类就不要再写了,(就是不要new隐藏),若是想修改父类的方法,经过abstract/virtual来标识!
三:迪米特法则
也叫最少知道原则,就是:一个对象应该对其余对象保持最少的了解,只与直接的朋友通讯。
他主要的职责就是关注类与类之间的交互,下降类与类之间的耦合,尽可能避免依赖更多的类型
举例说明:
有一个学生类,班级类,学校类
/// <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); } }
/// <summary> /// 班级 /// </summary> public class Class { public int Id { get; set; } public string ClassName { get; set; } public List<Student> StudentList { get; set; } public void ManageClass() { Console.WriteLine(" {0}Manage {1} ", this.GetType().Name, this.ClassName); foreach (Student s in this.StudentList) { s.ManageStudent(); } } }
1 /// <summary> 2 /// 学校 3 /// </summary> 4 public class School 5 { 6 public int Id { get; set; } 7 public string SchoolName { get; set; } 8 public List<Class> ClassList { get; set; } 9 10 public void Manage() 11 { 12 Console.WriteLine("Manage {0}", this.GetType().Name); 13 foreach (Class c in this.ClassList) 14 { 15 //遵循了迪米特,school直接跟本身的朋友classList通信,而不是跟本身的朋友的朋友通信 16 c.ManageClass(); 17 18 #region 违背了迪米特法则(跟本身的朋友的朋友通信) 19 //List<Student> studentList = c.StudentList; 20 //foreach (Student s in studentList) 21 //{ 22 // Console.WriteLine(" {0}Manage {1} ", s.GetType().Name, s.StudentName); 23 //} 24 #endregion 25 26 } 27 } 28
如今的关系是:一个学校有多个班级,每一个班级有多个学生,如今学校想要管理学生,学校能够直接跟班级通信,而后班级跟学生通信,这就是所谓的只与直接朋友通信,而后避免依赖更多类型(这个类型不包含:基类库BCL--框架内置)
其实类与类之间的关系能够总结为:
1:纵向:继承≈实现(最密切)
2:横向:聚合> 组合> 关联> 依赖(出如今方法内部)
依赖别人更少,也让别人了解更少,好比咱们项目中常常中用到的一些访问修饰符:
Private:私有
Protected:子类才能获取到,子类才能访问到
Internal :当前dll才能看见
Public:公开得
Protected internal 叠加要么是子类, 要么是相同类库
其实项目中能体现迪米特法则的地方好比:三层架构(UI-BLL-DAL),还有咱们习惯建立的中间层(UI-中间层--(调用不一样的业务逻辑进行组合)),另外还有门面模式
另外须要注意的是:单一职责法则只关注单类的功能;迪米特法则关注的是类与类之间的联系
四:依赖倒置原则(Dependence Inversion Principle)
依赖倒置原则:高层模块不该该依赖于低层模块,两者应该经过抽象依赖
那何为高层何为底层,通常来讲使用者为高层,被调用者为低层,具体仍是举例说明,咱们先建立一个手机:
public abstract class AbstractPhone { public int Id { get; set; } public string Branch { get; set; } public abstract void Call(); public abstract void Text(); } public class iPhone : AbstractPhone { public override void Call() { Console.WriteLine("User {0} Call", this.GetType().Name); } public override void Text() { Console.WriteLine("User {0} Call", this.GetType().Name); } }
接着咱们建立一个学生,而后学生玩手机:
1 public class Student 2 { 3 public int Id { get; set; } 4 public string Name { get; set; } 5 6 /// <summary> 7 /// 依赖细节 高层就依赖了底层 8 /// </summary> 9 /// <param name="phone"></param> 10 public void PlayiPhone(iPhone phone) 11 { 12 Console.WriteLine("这里是{0}", this.Name); 13 phone.Call(); 14 phone.Text(); 15 } 16 17 public void PlayLumia(Lumia phone) 18 { 19 Console.WriteLine("这里是{0}", this.Name); 20 phone.Call(); 21 phone.Text(); 22 } 23 24 public void PlayHonor(Honor phone) 25 { 26 Console.WriteLine("这里是{0}", this.Name); 27 phone.Call(); 28 phone.Text(); 29 } 30 }
而后上面的学生就是高层,而手机就是低层,而后学生玩手机不该该直接直接写PlayiPhone,PlayLumia,PlayHonor 等,显然这是依赖的细节,若是这样写,之后增长一个手机,则要在学生类中增长了一个play方法,这样就会使学生类反复修改而不稳定,因此咱们通常会定义一个基类为AbstractPhone,而后把各个型号通用的手机属性和功能都写在基类中,而后在学生中增长一个Play的方法以下:
1 public void Play(AbstractPhone phone) 2 { 3 Console.WriteLine("这里是{0}", this.Name); 4 phone.Call(); 5 phone.Text(); 6 }
这样的话之后增长手机的话只要继承AbstractPhone,则在外部均可以直接调用。由于父类出现的地方均可以用子类代替。
而后有人还说这个能够直接使用泛型来写以下:
public void PlayT<T>(T phone) where T : AbstractPhone { Console.WriteLine("这里是{0}", this.Name); phone.Call(); phone.Text(); }
这个T使用基类来进行约束,其实就等同于用父类参数类型!
调用的方法以下:
1 { 2 iPhone phone = new iPhone(); 3 student.PlayiPhone(phone); 4 student.PlayT(phone); 5 student.Play(phone); 6 } 7 { 8 Lumia phone = new Lumia(); 9 student.PlayLumia(phone); 10 student.PlayT(phone); 11 student.Play(phone); 12 } 13 { 14 Honor phone = new Honor(); 15 student.PlayHonor(phone); 16 student.PlayT(phone); 17 student.Play(phone); 18 }
而后这样写的好处主要分为如下两点:
1 :一个方法知足不一样类型的参数
2:还支持扩展,只要是实现了这个抽象,不用修改Student,稳定的同时又多了扩展
这就是所谓的面向抽象编程,可是面向抽象编程只适合于通用功能,若是你想要在子类中有特殊的操做,好比想要在iphone手机新增一个其它手机没有的方法A,而后play方法还传入AbstractPhone这个参数,这样是么有办法访问到A,这就是非通用的,就不该该面向抽象
那何为面向抽象,以及面向抽象的好处?
面向抽象就是:只要抽象不变,高层就不变!
面向抽象的好处:
面向对象语言开发,就是类与类之间进行交互,若是高层直接依赖低层的细节,细节是多变的,那么低层的变化就致使上层的变化;若是层数多了,底层的修改会直接水波效应传递到最上层,一点细微的改动都会致使整个系统从下往上的修改!
而面向抽象,若是高层和低层没有直接依赖,而是依赖于抽象,抽象通常是稳定的,那低层细节的变化扩展就不会影响到高层,这样就能支持层内部的横向扩展,不会影响其余地方,这样的程序架构就是稳定的
之后不少的设计模式都会跟抽象有关,好比咱们常常说的控制反转IOC就是一个很好的例子!依赖倒置原则(理论基础)---IOC控制反转(实践封装)---DI依赖注入(实现IOC的手段)!
五: 接口隔离原则(Interface Segregation Principle)
接口隔离原则则是:客户端不该该依赖它不须要的接口, 一个类对另外一个类的依赖应该创建在最小的接口上;
这个原则跟本身实际工做有很大的关系,总结下来能够经过如下几点来定义接口:
1: 既不能是大而全,会强迫实现没有的东西,也会依赖本身不须要的东西
2 :也不能一个方法一个接口,这样面向抽象也没有意义的
按照功能的密不可分来定义接口,
并且应该是动态的,随着业务发展会有变化的,可是在设计的时候,要留好提早量,避免抽象的变化
没有标准答案,随着业务和产品来调整的
3 :接口合并 Map--定位/搜索/导航 这种属于固定步骤,业务细节,尽可能的内聚,在接口也不要暴露太多业务细节(就是密切相连的能够放在一个接口来实现,而不是分多个接口实现功能)
六:开闭原则(Open Closed Principle)
开闭原则:对扩展开发,对修改关闭
开闭原则只是一个目标,并无任何的手段,也被称之为总则,其余5个原则的建议,就是为了更好的作到OCP, 开闭原则也是面向对象语言开发一个终极目标!
若是有功能增长/修改的需求能够经过优先级考虑如下修改:A:修改现有方法---B:增长方法---C:增长类---D:增长/替换类库