看大化设计模式,小菜与大鸟的对话真的是让人陷入其中,无法自拔啊!两个人只是在一起生活了一段时间,就把设计模式一一列举出来,而且恰到好处,和生活完美的结合起来,印象真的很深刻啊!
看大化模式,得到师姐的指导,大概浏览一遍,得到了不一样的收获,设计模式总共有六大原则,总共24中模式,正所谓万卷不离其宗,先来把原则了解清楚吧!
1)单一职责原则:
定义:
就一个类而言,应该仅有一个引起它变化的原因。通俗点,即一个类只负责一项职责。
问题来源:
如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。简单点就是类H负责两个不同的职责,职责T1,职责T2,当由于职责T1需求发生改变而需要修改类H时,就可能导致原本运行正常的T2发生故障。
解决方案:
分别建立两个类H1和H2,让H1完成T1的功能,H2完成T2的功能,这样两不耽误。
说明:
单一职责原则不只是面向对象编程所特有的,只要是模块化的设计,都使用单一职责原则。
2)依赖倒转原则:
定义:
高层模块不应该依赖低层模块,两个都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。
问题来源:
类A直接依赖与类B,假如要将类A修改为依赖类C,则必须通过修改类A的代码来完成(类A一般是高层模块,负责复杂的业务逻辑;类B和类C是底层模块,负责基本的原子操作;假如修改类A,则有可能带来危险,所以出现依赖倒转来解决)
问题解决:
把类A修改为依赖接口G,类B和类C各自实现接口G,则可以大大降低危险率。
举例:比如人类现在讲究营养均衡,要多吃水果和蔬菜,比如Max特别喜欢吃苹果,写一个苹果的代码,但是也不能单吃苹果啊!香蕉、蔬菜等等也是必不可少的,这就可以用到此原则,建立两个接口,People和Fruit(如果想加蔬菜,可以再建立一个接口),这样我们可以实现任意一个人吃任意的水果都不会影响其稳定性。
<strong><span style="font-family:SimHei;font-size:18px;"> static void Main(string[] args)
{
IPeople Max=new Max ();
IFruit apple=new apple ();
IFruit orange=new orange();
Max.eat (apple );
Max .eat (orange );
}
}
//人接口
interface IPeople
{
void eat( IFruit fruit);
}
interface IFruit //水果接口
{
string GetName(); //水果名字
}
class Max : IPeople //具体Max类
{
public void eat (IFruit fruit){
Console .WriteLine ("Max eat "+fruit.GetName ());
}
}
class apple:IFruit //具体苹果类
{
public string GetName(){
return "apple";
}
}
class orange:IFruit //具体橙子类
{
public string GetName(){
return "orange";
}
}</span></strong>
实现结果:
3)里氏代换原则:
定义:
子类型必须能够替换掉它们的父类型。说白点就是一个软件实体如果使用的是一个父类的话,那么一定使用于其子类,而且它察觉不出父类对象和子类对象的区别。也就是说,在软件里边,把父类都替换成它的子类,程序的行为没有变化。
说明:
看设计模式,会发现依赖倒转原则是实现高层模块依赖底层模式;里氏代换原则是实现高层模块和底层模块都应该依赖抽象。如下:
举例:就举例上边的水果营养的例子
<strong><span style="font-family:SimHei;font-size:18px;"> class Program显示结果:
{
static void Main(string[] args)
{
Fruit p=new Fruit ();
Fruit p1=new Import ();
Console .ReadKey ();
}
}
class Fruit
{
private int nPrice; //父类的私有成员
public Fruit ()
{
Console.WriteLine("我属于水果家族");
}
public void Say()
{
Console.WriteLine("我是一个营养丰厚的水果");
}
}
class Import :Fruit //进口水果继承水果
{
public Import ()
{
Console.WriteLine("我属于进口水果家族");
}
public void SayI()
{
Console.Write("我是一个进口的营养丰厚的水果");
}
}
class ImportApple:Import //进口苹果继承进口水果
{
public ImportApple ()
{
Console.WriteLine("我是进口苹果家族中的一员");
}
public void SayIA()
{
Console.WriteLine("我是一个进口的苹果");
}
}</span></strong>
如图所示,父类对象只继承访问父类的方法,而子类却也只可以访问父类的方法;也就是说明子类对象继承了父类对象的位置,满足了里氏替换原则。
4)迷米特法则:
定义:
如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。简单点就是尽量降低类与类之间的耦合。
联想:
从接触编程开始就知道一句话:高内聚低耦合,无论是面向过程还是面向对象,只有使各个模块之间的耦合降低,才能提高代码的复用率。而低耦合正是最少知识原则去完成的。
举例:爸爸的朋友过年了要来看他,这件事貌似和自己并没有多大的关系,只是过程中或许会设计到自己的学习成绩等等。实现代码如下:
<span style="font-family:SimHei;font-size:18px;"> public static void Main(string[] args)运行结果:
{
Friends friend = new Friends();
friend.VisitFather(new Friends.Father());
}
}
public class Friends //朋友
{
public void VisitFather(Father father) //朋友来看望父亲
{
Console.WriteLine("朋友说:");
father.takecarechild();
}
public class Father //父亲
{
private Danghter daughter = new Danghter();
public Danghter takecarechild()
{
Console.WriteLine("要好好培养你们之间的感情");
Console.WriteLine("父亲说:");
daughter.weAreFriend();
return daughter;
}
}
public class Danghter //女儿
{
public void weAreFriend()
{
Console.WriteLine("我们是好朋友");
}
}
}</span>
想要表达的就是朋友的意愿是通过父亲来表达自己对女儿的关心!而同时也和女儿分开来!避免了自己因为代沟和陌生感而打扰到女儿。
5)合成/聚合复用原则:
定义:
尽量使用合成/聚合,尽量不要使用类继承
优点(好处):
优先使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。
分析:
一般而言,如果两个类之间是“Has-A”的关系应使用组合或者聚合,如果是“Is-A”的关系可使用继承。“Is-A”表示一个类是另一个类的“一种”,而“Has-A”则表示某一个角色具有某一项责任。
因为每个品牌肯定都有自己的特定的软件,如小米,NOKIA等等都有着自己独特的软件,但软件并不是品牌的一部分,所以是聚合关系。
<span style="font-family:SimHei;font-size:18px;">static void Main(string[] args)如果要想增加音乐播放功能或者其他品牌的话,就可以直接增加其类就可以了,不会对其他有任何的影响。
{
HandsetBrand ab;
ab = new HandsetBrandN(); //品牌N的功能实现
ab.SetHandsetSoft(new HandsetGame());
ab.run();
ab.SetHandsetSoft(new HandsetAddressList());
ab.run();
ab = new HandsetBrandM(); //品牌M的功能实现
ab.SetHandsetSoft(new HandsetGame());
ab.run();
ab.SetHandsetSoft(new HandsetAddressList());
ab.run();
Console.WriteLine();
}
}
//手机软件
abstract class HandsetSoft
{
public abstract void Run();
}
//手机游戏
class HandsetGame:HandsetSoft
{
public override void Run()
{
Console.WriteLine ("运行手机游戏");
}
}
//手机通讯录
class HandsetAddressList:HandsetSoft
{
public override void Run()
{
Console.WriteLine("运行手机通讯录");
}
}
//手机品牌
abstract class HandsetBrand
{
protected HandsetSoft soft;
//设计手机软件
public void SetHandsetSoft(HandsetSoft soft) //品牌需要关注软件,所以得设计手机软件,以备运行使用
{
this.soft = soft;
}
public abstract void run();
}
//手机品牌N
class HandsetBrandN:HandsetBrand
{
public override void run()
{
soft.Run(); //运行
}
}
//手机品牌M
class HandsetBrandM :HandsetBrand
{
public override void run()
{
soft.Run();
}
}</span>
6)开放封闭原则:
定义:
软件实体(类,模块,函数)应该可以扩展,但是不可修改。也就是说对于扩展是开放的,而对于内部更改是封闭的。模块应尽量在不修改原代码的情况下进行扩展。
解决办法:
当软件需求变化时,尽量通过扩展软件实体的行为来实现变化而不是通过实现已有的代码来实现变化。
说明:
开闭原则是面向设计中最基础的设计原则,它指导我们如何建立稳定灵活的系统。其实不管是其他的五种设计原则还是24种模式他们的目的就是遵循开闭原则。
总结:
单一职责告诉我们实现类要职责单一;里氏替换原则告诉我们不要破坏继承体系;依赖倒置原则告诉我们要面向接口变成;秘密特法则告诉我们要降低耦合;合成/聚合复用原则告诉我们尽量不要使用类继承;而开闭原则是总纲,告诉我们要对扩展开放,对修改关闭。