目录
- 简介
-
- 设计模式是什么?
- 设计模式六大原则是什么?
- 设计模式有哪些?
- 单一职责原则(Single Responsibility Principle)
-
- 我们应该如何使用单一职责呢?
- 里氏替换原则(Liskov Substitution Principle)
-
- 什么是透明呢?
- 我们应该如何使用里氏替换原则呢?
- 迪米特法则 (Law Of Demeter)
-
- 我们应该如何使用迪米特法则呢?
简介
实际上真实项目很难全部遵循,更多的时候会有一些侧重性,设计模式六大原则要能灵活应用,离不开实践的锤炼和思考,把这个真的融入到骨子里面去了,设计确实会不一样的
设计模式是什么?
面向对象语言开发过程中,遇到种种的场景和问题,提出的解决方案和思路,沉淀下来设计模式是解决具体问题的套路
设计模式六大原则是什么?
面向对象语言开发过程中,推荐的一些指导性原则没有明确的招数,而且也经常会被忽视/违背也是前辈总结,也是为了站在前辈的肩膀上
设计模式有哪些?
- 单一职责原则(Single Responsibility Principle)
- 里氏替换原则(Liskov Substitution Principle)
- 迪米特法则 (Law Of Demeter)
- 依赖倒置原则(Dependence Inversion Principle)
- 接口隔离原则(Interface Segregation Principle)
- 开闭原则 (Open Closed Principle)
单一职责原则(Single Responsibility Principle)
类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。
{
Animal animal = new Animal("鸡");//呼吸空气
animal.Breath();
//animal.Action();
}
{
Animal animal = new Animal("牛");//呼吸空气
animal.Breath();
//animal.Action();
}
{
Animal animal = new Animal("鱼");//呼吸水
animal.Breath();
animal.Action();
}
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} 呼吸泥土");
}
//应该拆分了
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");
}
}
单一职责就是一个类只负责一件事儿。面向对象语言开发中类是一个基本单位,单一职责原则就是封装的粒度
上面这个例子写分支判断,然后执行不同的逻辑,其实这就违背了单一职责原则,但是功能是可以实现的
{
AbstractAnimal animal = new Chicken();
animal.Breath();
animal.Action();
}
{
AbstractAnimal animal = new Fish();
animal.Breath();
animal.Action();
}
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");
}
}
按父类+子类的拆分方式,每个类很简单,简单意味着稳定 意味着强大。 拆分后,也会造成代码量的增加,类多了,使用成本也高(理解成本)
我们应该如何使用单一职责呢?
如果类型足够简单,方法够少,是可以在类级别去违背单一职责。如果类型复杂了,方法多了,建议遵循单一职责原则。
- 方法级别的单一职责原则:一个方法只负责一件事儿(职责分拆小方法,分支逻辑分拆)
- 类级别的单一职责原则:一个类只负责一件事儿
- 类库级别的单一职责原则:一个类库应该职责清晰
- 项目级别的单一职责原则:一个项目应该职责清晰(客户端/管理后台/后台服务/定时任务/分布式引擎)
- 系统级别的单一职责原则:为通用功能拆分系统(IP定位/日志/在线统计)
里氏替换原则(Liskov Substitution Principle)
任何使用基类的地方,都可以透明(安全)的使用其子类。
什么是透明呢? 安全,不会出现行为不一致,下面会举例说明。
{
//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
//要么就只能在子类抛异常
}
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("忍者精神 ");
}
}
什么是透明呢?
在以上代码中我们可以看到Chinese people = new Hubei(); var people = new Hubei();这两个方法在调用SayHi方法时,结果是不同的。Chinese 调用的是父类的,var调用的是子类的。这样写会导致其他人再修改代码时掉入坑中,也是这个模式禁止的。
我们应该如何使用里氏替换原则呢?
-
父类有的,子类是必须有的;
如果出现了子类没有的东西,那么就应该断掉继承;(再来一个父类,只包含都有的东西)
-
子类可以有自己的属性和行为
子类出现的地方,父类不一定能代替
-
父类实现的东西,子类就不要再写了,(就是不要new隐藏)
有时候会出现意想不到的情况,把父类换成子类后,行为不一致。如果想修改父类的行为,通过abstract/virtual
- 在声明属性、字段、变量,尽量声明在父类中。
迪米特法则 (Law Of Demeter)
迪米特法则(最少知道原则):一个对象应该对其他对象保持最少的了解。
public static void Show()
{
Console.WriteLine("************************");
School school = new School()
{
SchoolName = "软谋教育",
ClassList = new List<Class>()
{
new Class()
{
ClassName="高级班",
StudentList=new List<Student>()
{
new Student()
{
StudentName="Tony"
},
new Student()
{
StudentName="风尘浪子"
}
}
}
}
};
school.Manage();
}
/// <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();
//Console.WriteLine(" {0}Manage {1} ", s.GetType().Name, s.StudentName);
}
}
}
/// <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);
}
}
School下有Class,Class下有Student。School不会直接与Student产生关系
我们应该如何使用迪米特法则呢?
1. 类与类之间的关系:
纵向:继承≈实现(最密切)
横向:聚合> 组合> 关联> 依赖(出现在方法内部)
2. 高内聚低耦合
降低类与类之间的耦合,只与直接的朋友通信,就是要尽量避免依赖更多类型。(基类库(BCL--框架内置的)的类型除外,例如:string,int)
3. 工作中有时候会去造一个中介/中间层
例如:门面模式 中介者模式 分层封装
上层UI下订单---订单系统&支付系统&仓储&物流
门面模式--上层交互门面--门面依赖子系统
三层架构:UI---BLL---DAL
4. 去掉内部依赖,降低访问修饰符权限
private,protected,internal,protected internal,public
5. 总之就是,依赖别人更少,让别人了解更少