推荐几本相关的书:
(1)Head First Design Patterns
曾经买Head First系列的时候买的一本书,是java语言的案例,但是完全不影响你了解设计模式。这系列的书就是有很多图,做快速了解建议买。
(2)大话设计模式
1个月前买的,看作者简介是名老师,里面就是菜鸟和大鸟的对话举出很多例子,案例也相当不错。这本书最起码让我感觉特别不错。
(3)重构与模式
这本是必须要看的一本书,前几张讲了什么是重构,什么是模式。然后两者之间的关系。后边是是讲设计模式的动机,做法,实例,变体。也不分什么创建,行为,结构什么的。最后一章是重构的实现。
单一职责原则告诉我们实现类要职责单一;里氏替换原则告诉我们不要破坏继承体系;依赖倒置原则告诉我们要面向接口编程;接口隔离原则告 诉我们在设计接口的时候要精简单一;迪米特法则告诉我们要降低耦合。而开闭原则是总纲,他告诉我们要对扩展开放,对修改关闭。
【开指的是对扩展开放,关指的对修改关闭。】
我把它理解为“一国两制”原则。一国两制怎么说:香港澳门继承了中国这个类,表示说:一个中国不可改变,但针对与港澳实际情况,他们实行的是资本主义经济。
【一个类应该只有一个发生变化的原因。】
高内聚低耦合这就是我们写程序的目标,但是很多时候高耦合会在不经意间就产生了,这大多是因为职责扩散造成的。这个原则最好理解,又最容易违背这个原则。原因就是职责这个家伙不好确认。
【抽象不应当依赖于细节,细节应当依赖于抽象;高层实现不依赖底层实现。】 想想让你封装一个类的时候你首先会做什么。会先封装接口,再写实现。{#总工说这样处理才是合理的。原因就在这#}。面向接口编程而非实现。这个原则在我看来也是面向对象设计的标志。
举个例子:usb是不是所有的的电脑都能通过usb接口连接。如果联想的usb接口和苹果的usb接口不一样,那么你买了一个200多的USB键盘,结果是不是就不能公用了。
【子类可以扩展父类的功能,但不能改变父类原有的功能】
里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
有这么一句话:里氏代换原则是继承复用的一个基础。
检验你是否遵循了里氏代换原则的方法:如果调用的是父类的话,那么换成子类也完全可以运行。
动物 dongwu=new 猫();其中【把猫换成狗】也是正常的就说明你是遵循这个原则的。
{注:我在网上看过一个“企鹅是鸟不会飞”的例子,这也是自己犯这个错误的原因。这例子在这不说了,你可以试着去找一下去。}
从字面上来讲就是一个不要把接口写的太臃肿。查资料大致说的就是有两种分离方式一种是“定制服务”和“角色隔离”。
在工作当中有没有这样的问题存在:同一个模块,因为没有安排得当两个人都去开发,最后一定是有个人白做了。所以有时候,项目管理软件就显的那么的有必要。
定制服务 : 大致来讲就是我针对一个客户端,我的一些方法放到一个接口里,另一个客户端我的一个类放在另一个接口里面。
角色隔离:是指一个客户端有多个方法,多个方法写多个接口。
【友情提醒:接口也不要分的太细,要不然结果就是接口太多。】
【我的理解就是:这个原则不希望类与类之间不要建立直接联系。】简单来说就是不和陌生人说话。类与类之间一定会存在互相调用的?网上查了一下,说可以用友元类来转达。
降低类本身和成员的访问权限,达到【低耦合,高内聚】是其目的。
【和 ISP 接口隔离原则一样,限制类与类之间的通信。 ISP 限制的是宽度,而 LoD 迪米特原则限制的是通信的广度和深度。】。
外观模式( Facade Pattern) 和中介者模式( Mediator Pattern )就使用了迪米特法则。
原则:确保一个类只有一个实例 , 并提供一个全局访问点
举例: 打印机就是最好的例子,打印就是纸打印一个对象多的话就进行排队。
主要解决:一个全局使用的类频繁地创建与销毁。
优点:1 、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。 2 、避免对资源的多重占用(比如写文件操作)。
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
//懒汉模式 public class SingletonClass{ private static SingletonClass instance=null; public static synchronized SingletonClass getInstance() { if(instance==null { instance=new SingletonClass(); } return instance; } private SingletonClass(){ } } //饿汉式 //对第一行static的一些解释 // 允许我们在一个类里面定义静态类。比如内部类(nested class)。 //把nested class封闭起来的类叫外部类。 //我们不能用static修饰顶级类(top level class)。 //只有内部类可以为static。 public class Singleton{ //在自己内部定义自己的一个实例,只供内部调用 private static final Singleton instance = new Singleton(); private Singleton(){ //do something } //这里提供了一个供外部访问本class的静态方法,可以直接访问 public static Singleton getInstance(){ return instance; } } // 双重锁的形式。 public class Singleton{ private static Singleton instance=null; private Singleton(){ //do something } public static Singleton getInstance(){ if(instance==null){ synchronized(Singleton.class){ if(null==instance){ instance=new Singleton(); } } } return instance; } }
原则:封装改变,既然要封装改变,自然也就要找到改变的代码,然后把改变的代码用类来封装和降低对象之间的耦合度
举例: 夏天到了,去撸串的季节到了。老板来10个板筋,5个腰子,20个串,10个鸡胗。。。。一会老板就给你上来了。这就是工厂模式。老板烤串就是工厂,你和你的兄弟们就是顾客。只需要照着单子点即可,不需要知道老板具体是怎么做的。
主要解决:主要解决接口选择的问题。
优点:1 、一个调用者想创建一个对象,只要知道其名称就可以了。 2 、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3 、屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
/// <summary> /// 单位信息工厂模式 /// </summary> public class unit_factory { public static unit create_unit_factory(string unitname) { unit unit = null; switch (unitname) { case "room": { unit = new unti_rooms(); break; } case "floor": { unit = new unti_floor(); break; } case "building": { unit = new unti_building(); break; } case "area": { unit = new unti_areas(); break; } } return unit; } } /// <summary> /// 单位信息基类 /// </summary> public class unit : wx_sbs_redis { public fee_power_query fee_power_query; public virtual result get_info() { result ret = null; return ret; } } /// <summary> /// 楼称派生类 /// </summary> class unti_floor : unit { string sign = fee_power_query.orgid + "|" + fee_power_query.uid + "|" + fee_power_query.area_id + "|" + fee_power_query.building_id + "|" + fee_power_query.cardno + "|" + fee_power_query.code; if (!fee_power_query.valid_floors()) return Result((int)errcode.Interval, fee_power_query.msg); List<cs_power> list = new List<cs_power>(); var recents = new fee_recents().pick("fee" + fee_power_query.uid, "power_floor"); var ret = new wxwebapi<i_gate_fee_power>().invoke(i => i.floors, gatapower); return ret; } } /// <summary> /// 房间派生类 /// </summary> class unti_rooms : unit { } /// <summary> /// 楼派生类 /// </summary> class unti_building : unit { } /// <summary> /// 校区派生类 /// </summary> class unti_areas : unit { }
原则:一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。他强调的是单个对象的变化。
【工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替 new 操作的一种模式 】
举例:
上面说到工厂模式,有一个问题,就是实现的调用要依赖于工厂类,如果扩展程序要修改工厂类,那么这样就违背了开闭原则。为此用到了抽象工厂模式。其实说道设计原则,抽象工厂模式更能体现的是里氏代换原则。
主要解决:主要解决接口选择的问题。
原则:为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。强调的是系列对象的变化。
优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。
/// <summary> /// 抽象工厂类,提供创建两个不同地方的鸭架和鸭脖的接口 /// </summary> public abstract class AbstractFactory { // 抽象工厂提供创建一系列产品的接口,这里作为例子,只给出了绝味中鸭脖和鸭架的创建接口 public abstract YaBo CreateYaBo(); public abstract YaJia CreateYaJia(); } /// <summary> /// 南昌绝味工厂负责制作南昌的鸭脖和鸭架 /// </summary> public class NanChangFactory : AbstractFactory { // 制作南昌鸭脖 public override YaBo CreateYaBo() { return new NanChangYaBo(); } // 制作南昌鸭架 public override YaJia CreateYaJia() { return new NanChangYaJia(); } } /// <summary> /// 上海绝味工厂负责制作上海的鸭脖和鸭架 /// </summary> public class ShangHaiFactory : AbstractFactory { // 制作上海鸭脖 public override YaBo CreateYaBo() { return new ShangHaiYaBo(); } // 制作上海鸭架 public override YaJia CreateYaJia() { return new ShangHaiYaJia(); } } /// <summary> /// 鸭脖抽象类,供每个地方的鸭脖类继承 /// </summary> public abstract class YaBo { /// <summary> /// 打印方法,用于输出信息 /// </summary> public abstract string Print(); } /// <summary> /// 鸭架抽象类,供每个地方的鸭架类继承 /// </summary> public abstract class YaJia { /// <summary> /// 打印方法,用于输出信息 /// </summary> public abstract string Print(); } /// <summary> /// 南昌的鸭脖类,因为江西人喜欢吃辣的,所以南昌的鸭脖稍微会比上海做的辣 /// </summary> public class NanChangYaBo : YaBo { public override string Print() { return "南昌的鸭脖"; } } /// <summary> /// 上海的鸭脖没有南昌的鸭脖做的辣 /// </summary> public class ShangHaiYaBo : YaBo { public override string Print() { return "上海的鸭脖"; } } /// <summary> /// 南昌的鸭架 /// </summary> public class NanChangYaJia : YaJia { public override string Print() { return "南昌的鸭架子"; } } /// <summary> /// 上海的鸭架 /// </summary> public class ShangHaiYaJia : YaJia { public override string Print() { return "上海的鸭架子"; } } /// <summary> /// 如果绝味又想开一家湖南的分店时,因为湖南喜欢吃麻的 /// 所以这是有需要有一家湖南的工厂专门制作 /// </summary> public class HuNanFactory : AbstractFactory { // 制作湖南鸭脖 public override YaBo CreateYaBo() { return new HuNanYaBo(); } // 制作湖南鸭架 public override YaJia CreateYaJia() { return new HuNanYajia(); } } /// <summary> /// 湖南的鸭脖 /// </summary> public class HuNanYaBo : YaBo { public override string Print() { return "湖南的鸭脖"; } } /// <summary> /// 湖南的鸭架 /// </summary> public class HuNanYajia : YaJia { public override string Print() { return "湖南的鸭架子"; } }
原则:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
举例:网上很多都用组装电脑来阐述建造者模式,一个电脑都有 cpu ,内存条,主板,硬盘。。。。这些部件显然在创建的时候也是一个复杂的过程,但是如果我们如果要组装一台电脑是不需要知道这个过程的。只需要把兼容部件拿过来组装即可。把部件和是否兼容分离就需要用到建筑者模式。
优点: 1 、建造者独立,易扩展。 2 、便于控制细节风险。
缺点:1 、产品必须有共同点,范围有限制。 2 、如内部变化复杂,会有很多的建造类。
public class Director { public void Construct(IBuilder builder) { builder.BuildPartA(); builder.BuildPartB(); } } public interface IBuilder { void BuildPartA(); void BuildPartB(); Product GetResult(); } public class ConcreteBuilder1 : IBuilder { private Product product = new Product(); public void BuildPartA() { product.Add("部件A"); } public void BuildPartB() { product.Add("部件B"); } public Product GetResult() { return product; } } public class ConcreteBuilder2 : IBuilder { private Product product = new Product(); public void BuildPartA() { product.Add("部件X"); } public void BuildPartB() { product.Add("部件Y"); } public Product GetResult() { return product; } } public class Product { IList<string> parts = new List<string>(); public void Add(string part) { parts.Add(part); } public string Show() { string str = string.Empty; foreach (string part in parts) { str = str + part; } return str; } }
原则:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
举例:西游记咱们应该都看过,悟空一根毫毛能变成千千万万的孙猴子,这就和这原型模式一样。通过一个原型得到多个和原型一模一样的新对象。
主要解决:在运行期建立和删除原型。
优点:1 、性能提高。 2 、逃避构造函数的约束。
缺点:1 、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。 2 、必须实现 Cloneable 接口。 3 、逃避构造函数的约束。
namespace Prototype { /// <summary> /// ICloneable接口充当了抽象原型类的角色 /// 具体原型类通常作为实现该接口的子类 /// </summary> public class PrototypeICloneable_Resume : ICloneable { private string name; private string sex; private string age; private WorkExperience work; public PrototypeICloneable_Resume(string name) { this.name = name; work = new WorkExperience(); } private PrototypeICloneable_Resume(WorkExperience work) { this.work = (WorkExperience)work.Clone(); } //设置个人信息 public void SetPersonalInfo(string sex, string age) { this.sex = sex; this.age = age; } //设置工作经历 public void SetWorkExperience(string workDate, string company) { work.WorkDate = workDate; work.Company = company; } //显示 public string Display() { string str = string.Format("{0} {1} {2}", name, sex, age); str = string.Format("工作经历:{0} {1}", work.WorkDate, work.Company); return str; } public Object Clone() { PrototypeICloneable_Resume obj = new PrototypeICloneable_Resume(this.work); obj.name = this.name; obj.sex = this.sex; obj.age = this.age; return obj; } } //工作经历 class WorkExperience : ICloneable { private string workDate; public string WorkDate { get { return workDate; } set { workDate = value; } } private string company; public string Company { get { return company; } set { company = value; } } public Object Clone() { return (Object)this.MemberwiseClone(); } } } namespace ProtoType { /// <summary> /// 提供了一个MemberwiseClone()方法用于实现浅克隆 /// 该方法使用起来很方便 /// 直接调用一个已有对象的MemberwiseClone()方法即可实现克隆 /// </summary> public class PrototypeMemberwise_Resume : ICloneable { private string name; private string sex; private string age; private WorkExperience work; public PrototypeMemberwise_Resume(string name) { this.name = name; work = new WorkExperience(); } //设置个人信息 public void SetPersonalInfo(string sex, string age) { this.sex = sex; this.age = age; } //设置工作经历 public void SetWorkExperience(string workDate, string company) { work.WorkDate = workDate; work.Company = company; } //显示 public string Display() { string str=string.Format("{0} {1} {2}", name, sex, age); str =str+ string.Format("工作经历:{0} {1}", work.WorkDate, work.Company); return str; } public Object Clone() { return (Object)this.MemberwiseClone(); } } //工作经历 class WorkExperience { private string workDate; public string WorkDate { get { return workDate; } set { workDate = value; } } private string company; public string Company { get { return company; } set { company = value; } } } }