六大设计模式原则
1. 单一职责原则(Single Responsibility Principle)
2. 里氏替换原则(Liskov Substitution Principle)
3. 依赖倒置原则(Dependence Inversion Principle)
4. 接口隔离原则(Interface Segregation Principle)
5. 迪米特法则 (Law Of Demeter)
6. 开闭原则 (Open Closed Principle)
设计模式:面向对象语言开发过程中,遇到种种的场景和问题,提出的解决方案和思路,沉淀下来设计模式是解决具体问题的套路
设计模式六大原则:面向对象语言开发过程中,推荐的一些指导性原则没有明确的招数,而且也经常会被忽视/违背
一,单一职责原则(Single Responsibility Principle)
单一职责原则:类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。一个类只负责一件事儿。面向对象语言开发,类是一个基本单位,单一职责原则就是封装的粒度。写分支判断,然后执行不同的逻辑,其实这就违背了单一职责原则,但是功能是可以实现的。
举例说明:
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 /// <summary> 14 /// 这个方法就挺不稳定,经常各种分支变化经常修改 15 /// </summary> 16 public void Breath() 17 { 18 if (this._Name.Equals("鸡")) 19 Console.WriteLine($"{this._Name} 呼吸空气"); 20 else if (this._Name.Equals("牛")) 21 Console.WriteLine($"{this._Name} 呼吸空气"); 22 else if (this._Name.Equals("鱼")) 23 Console.WriteLine($"{this._Name} 呼吸水"); 24 else if (this._Name.Equals("蚯蚓")) 25 Console.WriteLine($"{this._Name} 呼吸泥土"); 26 } 27 //BreathChicken BreathFish 28 29 //应该拆分了 30 public void Action() 31 { 32 if (this._Name.Equals("鸡")) 33 Console.WriteLine($"{this._Name} flying"); 34 else if (this._Name.Equals("牛")) 35 Console.WriteLine($"{this._Name} walking"); 36 else if (this._Name.Equals("鱼")) 37 Console.WriteLine($"{this._Name} Swimming"); 38 else if (this._Name.Equals("蚯蚓")) 39 Console.WriteLine($"{this._Name} Crawling"); 40 } 41 }
我们声明一个动物,但是每个动物的action是不一样的,顺着我们的思维模式,我们会在action中增加对应的if来判断,不同的动物有不同的动作,
类似于这样的,在一个方法中写分支判断,然后执行不同的逻辑,这就违背了单一职责原则,但是其实我们想要的功能完全能实现,如果种类比较少且不变的情况下,我们完全可以这样操作,但是如果种类比较多且经常容易发生改变,那我们这样写就有很大的隐患,因为其中的改变有可能会影响到其它的。
我们可以对其进行改变,比如我们可以先创建一个基类:
public abstract class AbstractAnimal { protected string _Name = null; public AbstractAnimal(string name) { this._Name = name; } public abstract void Breath();//呼吸的方法 public abstract void Action();//执行的动作 }
然后可以创建不同动物的类来继承于这个基类:
public class Chicken : AbstractAnimal { public Chicken(string name) : base(name) { } public Chicken() : base("鸡") { } public override void Breath() { Console.WriteLine($"{base._Name} 呼吸空气"); } public override void Action() { Console.WriteLine($"{base._Name} flying"); } }
public class Fish : AbstractAnimal { public Fish() : base("鱼") { } public override void Breath() { Console.WriteLine($"{base._Name} 呼吸水"); } public override void Action() { Console.WriteLine($"{base._Name} swimming"); } }
类似于上面的这两个类,每个类中实现自己的方法,一个类只负责自己的事情,且都比较单一,简单意味着稳定,意味着强大,这就是所谓的单一职责原则,那么究竟什么时候回使用单一职责原则呢?如果类型复杂,方法多,这样建议使用单一职责原则!
使用单一职责原则有自己的利弊:
1.拆分成父类+子类 每个类很简单,简单意味着稳定 意味着强大。
2. 拆分后,也会造成代码量的增加。
3.类多了,使用成本也高(理解成本)。与没有拆分前 都是放在一个类或者方法中实现,只用关注一个类;而拆分后,需要关注的类变多了,需要理解每个类的内容才能方便使用。
单一职责分为以下五种:
方法级别的单一职责原则:一个方法只负责一件事儿(职责分拆小方法,分支逻辑分拆)。
类级别的单一职责原则:一个类只负责一件事儿。
类库级别的单一职责原则:一个类库应该职责清晰。
项目级别的单一职责原则:一个项目应该职责清晰(客户端/管理后台/后台服务/定时任务/分布式引擎)。
系统级别的单一职责原则:为通用功能拆分系统(IP定位/日志/在线统计)。
二. 里氏替换原则(Liskov Substitution Principle)
任何使用基类的地方,都可以透明的使用其子类,这主要是指 继承+透明(安全,不会出现行为不一致)
继承:子类拥有父类的一切属性和行为,任何父类出现的地方,都可以用子类来代替,主要是因为:
1:父类有的,子类是必须有的(私有不继承);如果出现了子类没有的东西,那么就应该断掉继承;、
2:子类可以有自己的属性和行为,但是子类出现的地方,父类不一定能代替
3:父类实现的东西,子类就不要再写了,(就是不要new隐藏),如果想修改父类的行为,通过abstract/virtual
举例:
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("早上好,过早了么?"); } }
调用:
{ //People people = new Chinese(); Chinese people = new Chinese(); people.Traditional(); people.SayHi(); } { Chinese people = new Hubei(); people.Traditional(); people.SayHi(); } { var people = new Hubei(); people.Traditional(); people.SayHi(); }
比如:现在有个日本人 ,日本人是没有传统的,但是日本人是人。如果日本人也继承people类;
1 public class Japanese : People 2 { 3 //public int Id { get; set; } 4 //public string Name { get; set; } 5 //public new void Traditional() 6 //{ 7 // Console.WriteLine("忍者精神 "); 8 //throw new Exception(); 9 //} 10 //Traditional也会继承 但是Japanese又没有Traditional 11 public void Ninja() 12 { 13 Console.WriteLine("忍者精神 "); 14 } 15 16 }
可以通过抛出异常的方式解决 日本人没有传统这个方法,或者是新建一个类,日本人继承新的类,或者是在方法前加 new 关键字来隐藏 public new void Traditional();
public class Animal//让日本人继承自Animal { public int Id { get; set; } public string Name { get; set; } }
1 public class Japanese : Animal 2 { 3 public int Id { get; set; } 4 public string Name { get; set; } 5 public new void Traditional() 6 { 7 Console.WriteLine("忍者精神 "); 8 throw new Exception(); 9 } 10 //Traditional也会继承 但是Japanese又没有Traditional 11 public void Ninja() 12 { 13 Console.WriteLine("忍者精神 "); 14 } 15 16 }
注意的是:如果是普通的方法,以左边为主,就是左边是什么类,就调用谁的普通方法(编译时决定),如果是abstract或者virtual则是以右边为主(运行时决定),所以:父类有的方法,子类就不要再写了,(就是不要new隐藏),如果想修改父类的方法,通过abstract/virtual来标识!
三,迪米特法则 Law Of Demeter
迪米特法则:最少知道原则,一个对象应该对其他对象保持最少的了解。只与直接的朋友通信。
举例:
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 16 c.ManageClass();//1 遵循了迪米特 17 //List<Student> studentList = c.StudentList; 18 //foreach (Student s in studentList) 19 //{ 20 // Console.WriteLine(" {0}Manage {1} ", s.GetType().Name, s.StudentName); 21 //}//2 违背了迪米特法则 22 23 } 24 } 25 26 27 }
1 /// <summary> 2 /// 班级 3 /// </summary> 4 public class Class 5 { 6 public int Id { get; set; } 7 public string ClassName { get; set; } 8 9 public List<Student> StudentList { get; set; } 10 11 12 public void ManageClass() 13 { 14 Console.WriteLine(" {0}Manage {1} ", this.GetType().Name, this.ClassName); 15 foreach (Student s in this.StudentList) 16 { 17 s.ManageStudent(); 18 //Console.WriteLine(" {0}Manage {1} ", s.GetType().Name, s.StudentName); 19 } 20 21 } 22 23 24 }
1 /// <summary> 2 /// 学生 3 /// </summary> 4 public class Student 5 { 6 public int Id { get; set; } 7 public string StudentName { get; set; } 8 public int Height { private get; set; } 9 10 public int Salay; 11 12 public void ManageStudent() 13 { 14 Console.WriteLine(" {0}Manage {1} ", this.GetType().Name, this.StudentName); 15 } 16 }
类与类之间的关系:
纵向:继承≈实现(最密切)
横向:聚合> 组合> 关联> 依赖(出现在方法内部)
高内聚低耦合
迪米特法则,降低类与类之间的耦合 只与直接的朋友通信,就是要尽量避免依赖更多类型 基类库(BCL--框架内置的)的类型除外
迪米特,也会增加一些成本,工作中有时候会去造一个中介/中间层,门面模式 中介者模式 分层封装
项目中应用场景:上层UI下订单---订单系统&支付系统&仓储&物流 门面模式--上层交互门面--门面依赖子系统 三层架构:UI---BLL---DAL
去掉内部依赖
降低访问修饰符权限
private
protected
internal
protected internal 子类或者同类库
public
迪米特,依赖别人更少,让别人了解更少