目录
简介
实际上真实项目很难全部遵循,更多的时候会有一些侧重性,设计模式六大原则要能灵活应用,离不开实践的锤炼和思考,把这个真的融入到骨子里面去了,设计确实会不一样的
设计模式是什么?
面向对象语言开发过程中,遇到种种的场景和问题,提出的解决方案和思路,沉淀下来设计模式是解决具体问题的套路
设计模式六大原则是什么?
面向对象语言开发过程中,推荐的一些指导性原则没有明确的招数,而且也经常会被忽视/违背也是前辈总结,也是为了站在前辈的肩膀上
设计模式有哪些?
- 单一职责原则(Single Responsibility Principle)
- 里氏替换原则(Liskov Substitution Principle)
- 迪米特法则 (Law Of Demeter)
- 依赖倒置原则(Dependence Inversion Principle)
- 接口隔离原则(Interface Segregation Principle)
- 开闭原则 (Open Closed Principle)
依赖倒置原则(Dependence Inversion Principle)
高层模块不应该依赖于低层模块,二者应该通过抽象依赖。
{
//面向抽象后,不能使用子类的特别内容
Mi student= new Mi();
student.Play(phone);
student.PlayT(phone);
//如果传递的是Mi,Bracelet是有的,但是方法确实不能用
//编译器决定了是不能用Bracelet的(dynamic/反射是可以调用的)
//不能常规调用,这个问题是解决不了的,
//因为面向抽象不止一个类型,用的就是通用功能;非通用的,那就不应该面向抽象
}
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
/// <summary>
/// 依赖细节 高层就依赖了底层
/// </summary>
/// <param name="phone"></param>
public void PlayMi(Mi phone)
{
Console.WriteLine("这里是{0}", this.Name);
phone.Call();
phone.Text();
phone.Bracelet();//要用手环功能
}
public void Play(AbstractPhone phone)
{
Console.WriteLine("这里是{0}", this.Name);
phone.Call();
phone.Text();
//phone.Bracelet();
}
public void PlayT<T>(T phone) where T : AbstractPhone
{
Console.WriteLine("这里是{0}", this.Name);
phone.Call();
phone.Text();
}
}
public class Mi : AbstractPhone
{
public override void Call()
{
Console.WriteLine("User {0} Call", this.GetType().Name);
}
public override void Text()
{
Console.WriteLine("User {0} Text", this.GetType().Name);
}
public void Bracelet()
{
Console.WriteLine("User {0} Bracelet", this.GetType().Name);
}
}
public abstract class AbstractPhone
{
public int Id { get; set; }
public string Branch { get; set; }
public abstract void Call();
public abstract void Text();
}
像PlayMi方法就是高层直接依赖了底层。使用泛型或抽象类型就可以避免这个问题。但面向抽象后,不能使用子类的特别内容,若一定要使用可以通过反射的方式。
我们应该如何使用依赖倒置原则呢?
- 面向抽象,只要抽象不变,高层就不变
- 面向对象语言开发,就是类与类之间进行交互,如果高层直接依赖低层的细节,细节是多变的,那么低层的变化就导致上层的变化;如果层数多了,底层的修改会直接水波效应传递到最上层,一点细微的改动都会导致整个系统从下往上的修改
- 面向抽象,如果高层和低层没有直接依赖,而是依赖于抽象,抽象一般是稳定的,那低层细节的变化扩展就不会影响到高层,这样就能支持层内部的横向扩展,不会影响其他地方,这样的程序架构就是稳定的
- 依赖倒置原则(理论基础)—IOC控制反转(实践封装)—DI依赖注入(实现IOC的手段)
接口隔离原则(Interface Segregation Principle)
客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上;
Student student = new Student()
{
Id = 191,
Name = "张三"
};
{
IExtendVideo camera = new Camera();
student.Video(camera);
}
{
IExtendHappy extend = new TV();
student.Happy(extend);
}
{
IExtendGame extend = new PSP();
student.Happy(extend);
}
public interface IExtend
{
//void Photo();
//void Online();
//void Game();
void Record();
//void Movie();
void Map();//
void Pay();
}//都拆成一个方法一个接口
//电视--上网 玩游戏
public interface IExtendHappy : IExtendGame
{
void Online();
//void Game();
}
//掌中游戏机:俄罗斯方块--玩游戏不能上网
public interface IExtendGame
{
void Game();
}
public interface IExtendVideo
{
void Photo();
void Movie();//打开相机--切换模式--start--Suspend--End
}
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public void Video(IExtendVideo extend)
{
extend.Photo();
extend.Movie();
}
public void Happy(IExtendGame extend)
{
extend.Game();
}
}
尽量少声明大而全的接口,将接口更加细致的拆分,当然都拆成一个方法一个接口,肯定也不好!可以参考官方提供的类型例子
例如:List<> 它继承Dictionary,IList<T> (索引相关),ICollection<T> 集合相关操作,IEnumerable<T> 迭代器foreach
我们应该如何使用接口隔离原则呢?
- 既不能是大而全,会强迫实现没有的东西,也会依赖自己不需要的东西
- 也不能一个方法一个接口,这样面向抽象也没有意义的。按照功能的密不可分来定义接口,而且应该是动态的,随着业务发展会有变化的,但是在设计的时候,要留好提前量,避免抽象的变化。这个没有标准答案,随着业务和产品来调整的。
- 接口需要进行合并 ()例如:Map接口 需要继承 定位/搜索/导航) 这种属于固定步骤,业务细节,尽量的内聚,在接口也不要暴露太多业务细节
开闭原则 (Open Closed Principle)
对扩展开发,对修改关闭
面向对象语言是一种静态语言,最害怕变化,会波及很多东西又会涉及到全面测试。所以最理想就是新增类,对原有代码没有改动,原有的代码才是可信的。
开闭原则只是一个目标,并没有任何的手段,也被称之为总则。其他5个原则的建议,就是为了更好的做到OCP,也就是最终的目标——减少对原有代码的改动。
我们应该如何使用开闭原则呢?
按照修改的波及来排名:
修改现有方法 > 增加方法 > 增加类 > 增加/替换类库
修改原有的方法对项目的影响最大。替换类DLL对项目影响最小。