1、设计模式学习笔记(一)面向对象设计模式与原则 今天听了C#面向对象设计模式纵横谈(1):面向对象设计模式与原则课程。总结了一些笔记。首先介绍了什么是设计模式:设计模式描述了软件设计过程中某一类常见问题的一般性的解决方案。下面主要讨论面向对象设计模式。面向对象设计模式描述了类与相互通信的对象之间的组织关系。目的是应对变化、提高复用、减少改变。那到底什么是对象:1、从概念层面讲,对象是某种拥有职责的抽象;2、从规格层面讲,对象是一系列可以被其他对象使用的公共接口;3、从语言实现层面来看,对象封装了代码和数据(也就是行为和状态)。对于这三点,我理解最深的应该是第三点。这可能和我把大多精力放在了代码实
2、现上有关,然而忽略了编程的的思想。如果我们抛开代码的实现来看对象的概念,那么它应该就像一个具体的物体,比如说:榔头,从概念层面讲,榔头有它的职责,也就是它是做什么用的(用来砸钉子,当然还会有其他用途,如防身),从规格层面讲,比如人使用榔头砸钉子。面向对象的设计模式有三大原则:1、这对接口编程,而不是针对实现编程。在知道设计模式之前,我对接口的出现很不理解。不明白为什么这个什么都不能实现的东西会存在,当然,也对多态表示茫然。实际上我是还没有理解面向对象编程的思想。在对设计模式略有了解后发现接口的确是一个好东西,用它实现多态的确减少了代码的修改。比如说在Head First Design Patt
3、erns中有一个例子,说一个有关鸭子的游戏。游戏当中有很多种的鸭子,如:野鸭,木头鸭,鸭子模型。我们首先会想到做一个抽象类:abstract class Duck,Duck当中有很多的抽象属性和方法,如quack。我们用子类继承的时候都会实例化这个方法。public abstract class Duckpublic abstract void quack()public class MallardDuck:Duckpublic override void quack()Console.Write(I can quack);当程序成型后,我们有了很多种鸭子,突然,我们发现有的鸭子会飞,我们会在
4、Duck中在加上一个抽象方法abstract void fly();于是我们不得不在所有的子类当中添加fly的实现,有人会说,如果我们在Duck中直接添加fly的实现,不久不用在子类中添加实现了吗?那老板就该问你:你见过木头鸭子满天飞(哦,天啊!木头鸭子也飞起来了,这是什么世界!)。对不起老板,现在咱们都见到了。这时我们换一种想法,如果我们把这些方法都提取出来,把它变成Duck的成员,好像问题就会简单些。哈哈,好像扯的有点远了,现在回来接着记我的笔记。2、优先使用对象组合,而不是类的继承。这就使说多使用“has a”,少使用“is a”,哈哈,我又想说回刚才的例子。换个角度考虑Duck及其功能
5、,我们设计一个fly的接口和一些具体的飞行方法。public interface FlyBehaviorvoid fly();public class FlyWithWing:FlyBehaviorpublic void fly()Console.Write(I can flyn);public class FlyNoWay:FlyBehaviorpublic void fly()Console.Write(I cant flyn); 好了,对于Duck来说,现在它应该有一个(has a)fly的方法public abstract class Duckpublic Duck()public F
6、lyBehavior flybehavior; 现在我们再来实现两种鸭子public class ModelDuck:Duckpublic ModelDuck()flybehavior = new FlyNoWay();public class MallardDuck:Duckpublic MallardDuck()flybehavior = new FlyWithWing();这样如果要是在加上某种行为的话,我们就不必在每一种鸭子上下功夫。把注意力放在我们关心的鸭子品种上(别太使劲关注,小心禽流感,“阿切!”)。3、封装变化点,实现松耦合,这点不用多说了。课程中提到,编码当中的设计模式使用不
7、是我们在编程之初就定下来的,应该是重构得到设计模式(Refactoring to Patterns)。哦,原来是这样,也好理解。在编码中遇到问题,然后想想应对方式。哈哈,我原来认为开始编程时就指定我们用什么设计模式呢。下面说说设计原则:1、单一职责原则(SRP):一个类应仅有一个引起它变化的原因。2、开放封闭原则(OCP):类模块应可扩展,不可修改。这里要说明一下,扩展和修改是不同的。比如:我们要在加一种ModelDuck,那么我们写一个ModelDuck的类继承Duck,这叫扩展,不是修改。什么是修改,就好像我们开始说的那种作法,为了加一个fly的功能,我们要把所有的子类中加入不同的实现,这
8、叫修改。3、Liskov替换原则:子类可替换基类。4、依赖倒置原则:高层模块不依赖于低层模块,二者都依赖于抽象。还是刚才的例子:Duck是一个高层模块,fly是低层模块。Duck不依赖于fly,高层模块的改变慢,而低层模块的改变慢。抽象不应依赖于实现细节,实现细节应依赖于抽象。fly是一个抽象,它不依赖如何飞行。5、接口隔离原则:不强迫客户程序依赖于它们不用的方法(有道理,木头鸭子不会飞为什么要让它实现飞的功能。)最后有人提问接口和抽象类的区别:接口可以多继承,抽象类只能但继承。接口定义组件间的合同。使用抽象类为一个is a的关系。设计模式学习笔记(二)Singleton单件模式 这是我写模式
9、设计的第二篇,首先来说说设计模式的分类。基本的23种设计模式从目的上可分为三种:1、 创建型(Creational)模式:负责对象创建。2、 结构型(Structural)模式:处理类与对象间的组合,可以解决一些继承依赖性的问题3、 行为型(Behavioral)模式:类与对象交互中的职责分配,可以解决组件间如何和交互,隔离变化。下面来说说单件模式:首先说说单件模式产生的动机,也就是为什么会出现单件模式。有一些类在系统中只存在一个实例才能确保他们的逻辑正确性以及良好的效率。这时我想到我遇到的一个问题。我曾经遇到一个WinForm程序,运行后出现一个登陆框,输入用户名密码后点击登陆,然后显示一个
10、登陆后的界面。但是点击登陆后,程序要做一些操作,由于这段操作用时相对较长,在不经意时,我有点击了一次登陆按钮,最后出现了两个对话框。如:我现在有两个Form窗体Form1和Form2。Form1上有一个按钮用来打开Form2并隐藏自己。我们可以这样写:private void button1_Click(object sender, System.EventArgs e)Form2 form = new Form2();form.Show();this.Hide();如果我们在显示Form2前由一些比较耗时的操作。如:我们让线程的沉睡10秒在显示Form2,当我们在线程沉睡时继续点击Form1
11、上的Button,有可能就会出现两个Form2的窗体。(我试过可以出现两个Form2,如果你有心试但没事出来别拿西红柿砍我,哈哈)private void button1_Click(object sender, System.EventArgs e)Thread.Sleep(10000);Form2 form = new Form2();form.Show();this.Hide();这种情况出现不能怪客户多点了一下,也不能说是编译器不够智能,应该是我们程序上的Bug,我想这种情况用单件模式应该可以解决。单件模式的使用意图就是:保证一个类仅有一个实例,并提供一个该实例全局的访问点(这句话当然
12、不是我先说的,是引用Gof在设计模式中的一句话)那类的设计者如何绕过常规的构造器来实现单件模式呢?下面就来谈谈单件模式的实现。单件模式在结构上使用了景泰方法来约束构造器(也就是构造函数)创建对象。在单线程的情况下:私有化构造函数,使类的使用者调用不到这个构造函数来new一个实例。类型中可以自己new一个实例。类中创建一个静态私有变量和Static公有属性。在公有属性中实现此类的实例化。这样在第一次请求时创建此对象。代码如下:class Singletonprivate static Singleton _instance;private Singleton()public static Sin
13、gleton f_Instancegetif(_instance = null)_instance = new Singleton();return _instance;我在main函数中写入如下程序来查看一下这样写是否有效:static void Main(string args)Singleton t1 = Singleton.f_Instance;Singleton t2 = Singleton.f_Instance;Console.Write(object.ReferenceEquals(t1,t2);Console.Read();控制台显示为True,开来还是有效的。当然在Main中
14、我也试过这样写:Singleton t1 = new Singleton(),编译时告诉我Singleton()不可访问(当然,人家是私有的,不是自家人当然不见)这种单线程下的单件模式有几点要注意:1、 构造器私有化(如果要此类被继承,可以用protected声明构造器)2、 不要支持IClinieable接口,因为会导致多个对象实例的出现3、 不能支持序列化4、 单件模式只考虑了对象创建的管理,没有考虑对象的销毁管理(创建自己的对象,销毁的事交给垃圾回收器吧)5、 不能应对多线程环境,因为会导致多个对象实例的出现那在多线程下如何实现呢?代码如下:class SingletonMuli/多线程
15、Singleton模式private static volatile SingletonMuli _instance;/volatile是为了让编译器对此代码编译后的位置不进行调整private SingletonMuli()private static object lockHelper = new object();/辅助器,不参与对象构建public static SingletonMuli f_Instancegetif(_instance = null)lock(lockHelper)if(_instance = null)/双检查_instance = new SingletonM
16、uli();return _instance;当然还有一些更简单的实现方法,如:class Singleton1/可以用在多线程环境public static readonly Singleton1 _instance = new Singleton1();private Singleton1()其中要提到的是在_instance私有字段的实例化叫做“内联初始化”。内联初始化是指在声明时。实际上面的代码上相当于如下代码:Public static readonly Singleton1 _instance;Static Singleton()/静态构造函数_instance = new Sin
17、gleton();/私有构造器Private Singleton()内联初始化时会先执行静态构造器,如果没有静态构造函数,系统会默认一个。在访问此静态字段时执行静态构造器生成。静态构造器保证了在多线程时只有一个线程执行,自动加锁。当然,第二种实现方式也有一些缺点,如:静态构造器必须是私有的、无参的。不过也可以用其他的方式解决这类问题。如可以用方法属性实现扩展或修改私有构造器。现在我们可以回来看看我开始说的那两个Form的问题,我们现在可以这样实现:private static Form2 form;private void button1_Click(object sender, System
18、.EventArgs e)Thread.Sleep(10000);object lockhelp = new object();if(form = null)lock(lockhelp)if(form = null)form = new Form2();form.Show();this.Hide();这样问题就解决了(我是没有点出来第二个Form2,如果那位点出来了,给我发Email,我请她/他在天津的烤鸭)单件模式实际上是利用控制对象创造过程来控制对象的创造个数的方法,我们可以对其进行扩展,不是让他只生成一个对象,可以让他只生成几个对象,这样可以实现对象池。单件模式的核心是:如何控制用户使用
19、new对一个类的实例构造器的任意调用。设计模式学习笔记(三)Abstract Factory抽象工厂模式 抽象工厂是一种创建型模式,是为了解决实例化时所带来的问题。我们先来看看是什么问题,有的时候我们会遇到这种情况,我们需要一系列的对象。举个例子,有一系列BMW汽车零部件的对象:轮子bmwwheel,油箱bmwoilbox,在一个管理函数中调用它们,代码如下class BMWWheelpublic BMWWheel(); class BMWOilboxpublic BMWOilbox();public void Manage()BMWOilbox oilbox = new BMWOilbox(
20、);BMWWheel wheel = new BMWWheel();如果现在需求变了,我们要用大众一汽BORA的零件,不用BMW的,那么我们除了要再加上相应的零件对象外还要将Manage函数中的对象更改为BORA的零件对象。那这时发现new会带来了一些问题:实现依赖,不能应对具体实例化类型的变化。如何解决这类问题呢?封装变化点。(没有变化的就不需要封装)工厂模式的缘起:1、变化点在“对象创建”,因此就封装“对象创建”2、面向接口编程简单工厂问题:1、不能应对“不同系列对象”的变化。如:我们要在上面的代码中加上其他的对象就不能很好的应对了2、使用面向对象国内的技术来封装变化点动机:在软件系统中,
21、经常面临着“一系列相互依赖的对象”的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。面对这种问题,我们想绕过常规的对象创建方法,提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合。对于“紧耦合”,我原来是不喜欢这个词的,但是今天明白了,不是程序紧耦合不好,而是面对频繁变化的需求,紧耦合会使程序的编写变得很吃力。如果面对一个不变的需求,松耦合和紧耦合在代码编写上应该是没什么区别的。设计模式中解释这种模式的意图是:提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定他们的具体类。下面我们来看看如何使用抽象工厂模式完成对这种变化的封装:首先
22、我们的需求是BMW的车轮和油箱,当然他们要继承各自的基类,代码如下abstract class AbstractWheelpublic AbstractWheel()/Console.Write(Create a AbstractProduct);abstract class AbstractOilBoxpublic AbstractOilBox()class BMWWheel:AbstractWheelpublic BMWWheel()Console.Write(Create a BMWwheel);class BMWOilBox:AbstractOilBoxpublic BMWOilBox
23、()Console.Write(Create a BMWOilBox);然后,我们在建立一个生产这些零件的工厂,它继承自一个抽象工厂/抽象工厂abstract class AbstractFactoryabstract public AbstractWheel CreatWheel();abstract public AbstractOilBox CreatOilBox();class BMWFactory:AbstractFactorypublic override AbstractWheel CreatWheel()return new BMWWheel();public override
24、 AbstractOilBox CreatOilBox()return new BMWOilBox();现在我们在Main函数中调用它们:static void Main(string args)AbstractFactory factory = null;factory = new BMWFactory();factory.CreatWheel();Console.Write(n);factory.CreatOilBox();Console.Write(n);Console.Read();显示结果:Create a BMWwheelCreate a BMWOilBox现在我们想不用BMW的零
25、件,用BORA的零件了,先写一些BORA零件的类:class BORAWheel:AbstractWheelpublic BORAWheel()Console.Write(Create a BORAWheel);class BORAOilBox:AbstractOilBoxpublic BORAOilBox()Console.Write(Create a BORAOilBox);然后我们再创建BORA零件的工厂:class BORAFactory:AbstractFactorypublic override AbstractWheel CreatWheel()return new BORAWh
26、eel();public override AbstractOilBox CreatOilBox()return new BORAOilBox();再来看看如何在Main函数中修改使其调用BORA的零件;我们只要在将Main中的factory对象实例化为BORA的工厂BORAFactory就可以了:static void Main(string args)AbstractFactory factory = null;factory = new BORAFactory();factory.CreatWheel();Console.Write(n);factory.CreatOilBox();Co
27、nsole.Write(n);Console.Read();结果如下:Create a BORAWheelCreate a BORAOilBoxAbstract Factory模式的几个要点:1、如果没有应对“多系列对象构建”的需求变化,则没有必要使用Abstract Factory模式。2、“系列对象”指的是这项对象之间有相互依赖、或作用的关系。3、Abstract Factory模式主要在于应对“新系列”的需求变动。缺点是难以应对“新对象”的需求变动。这一点应该注意,就像前面说的,如果我们现在要在加入其他系列的类,代码的改动会很大。4、Abstract Factory模式经常和Factor
28、y Method模式共同组合来应对“对象创建”的需求变化。设计模式学习笔记(四)Builder生成器模式 Builder生成器模式是一种创建型模式,它主要是应对项目中一些复杂对象的创建工作。所谓“复杂对象”,是只:此对象中还含有其它的子对象。Builder模式所面对的情况是:各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将他们组合在一起的算法却相对稳定。简单的说:子对象变化较频繁,对算法相对稳定。这是解决一个复杂对象的创建工作,现在变化的部分和相对稳定的部分已经明确,我们要做的是隔离变化,如何将子对象和算法隔离是要解决的问题。设计模式中说道:
29、将一个复杂对象的构建与其表示向分离,使得同样的构建过程可以创建不同的表示。我们现在定义一个场景:还是选择汽车,BMW和BORA。试想一下,如果我们用较为普通的写法可以写成如下代码:public static void Main()Car car = new Car();Console.Write(Wheel: + car._wheel + n);Console.Write(OilBox: + car._oilBox + n);Console.Write(Body: + car._body + n);Console.Read();public class Carpublic string _wh
30、eel = “BMWWheel”;public string _oilbox = “BMWOilBox”;public string _body = “BMWBody”;当我们不确定或因需求变化而改变对汽车品牌的选择时,我们也许会频繁的更改Car类中的实现。现在我们用Manager来管理汽车的构建。当然是构建一个抽象的类对象(对象如下图的builder,抽象类为AbstractBuilder),对于BMW和BORA的构建类型继承AbstractBuilder类首先我们先来编写AbstractBuilder抽象类的实现,代码如下:public abstract class AbstractBui
31、lderpublic string _wheel;public string _oilBox;public string _body;public abstract void BuildWheel();public abstract void BuildOilBox();public abstract void BuildBody();public abstract Car GetCar(); public abstract class Car然后我们再来实现BMW和BORA的构建,也就是Builder模式中频繁变化的部分,他们都要继承AbstractBuilderBMW:public cla
32、ss BMWBuilder:AbstractBuilderpublic BMWBuilder()/ TODO: 在此处添加构造函数逻辑/public override void BuildWheel()_wheel = BMWWheel;public override void BuildOilBox()_oilBox = BMWOilBox;public override void BuildBody()_body = BMWBody;public override Car GetCar()return new BMWCar();public class BMWCar:CarBORA:pub
33、lic class BORABuilder:AbstractBuilderpublic BORABuilder()/ TODO: 在此处添加构造函数逻辑/public override void BuildWheel()_wheel = BORAWheel;public override void BuildOilBox()_oilBox = BORAOilBox;public override void BuildBody()_body = BORABody;public override Car GetCar()return new BORACar();public class BORAC
34、ar:Car现在我们使用一种Manager方法来管理汽车的构建,这也就是Builder模式中提到的相对稳定的算法,我用一个类来实现:private class CarManagerpublic BuilderClass.Car CreateCar(BuilderClass.AbstractBuilder builder)builder.BuildBody();builder.BuildOilBox();builder.BuildWheel();Console.Write(Wheel: + builder._wheel + n);Console.Write(OilBox: + builder._
35、oilBox + n);Console.Write(Body: + builder._body + n);Console.Read();return builder.GetCar();现在我们可以在客户程序中调用这个Manager来构建Car:public static void Main()CarManager manager = new CarManager();BuilderClass.Car car = manager.CreateCar(new BuilderClass.BORABuilder();结果如下:Wheel:BORAWheelOilBox:BORAOilBoxBody:B
36、ORABody如果我们现在要换成BMW,我们只要在Main()函数中改变manager.CreateCar中的参数就可以:public static void Main()CarManager manager = new CarManager();BuilderClass.Car car = manager.CreateCar(new BuilderClass.BMWBuilder();结果如下:Wheel:BMWWheelOilBox:BMWOilBoxBody:BMWBody这样,经过简单的修改可以实现对不同Car的构建。如果我们还有其他汽车的实现只要将他的类继承AbstractBuild
37、er,然后在Main()中修改就可以。说道这里,我想起了在第一篇中提到的设计模式中的“开-闭原则”,这个方式是很符合这个原则的,对代码进行了扩展,以减少了代码修改量。而且我们还有很多方法可以利用,如:WebConfig中的appSettings来动态的配置,从数据库中读取,或利用依赖方式动态生成。现在我们再来看看Builder模式的几个要点:Builder模式主要用于构建一个复杂的对象,但这个对象构建的算法是稳定的,对象中的各个部分经常变化。Builder模式主要在于应对复杂对象各个部分的频繁需求变动。但是难以应对算法的需求变动。这点一定要注意,如果用错了,会带来很多不必要的麻烦。课程中还提到
38、了.Net中的Builder模式的应用。如:Page类中的OnInit()等方法的实现。我们在写一个Web页面的时候。他的codebehind代码都是继承System.Web.UI.Page基类的。OnInit()函数是可以重写的设计模式学习笔记(五)FactoryMethod工厂模式 刚开始接触设计模式时就常常听到同事提起工厂模式,那时也看过一些人写的Blog,但是往往把注意力放在代码的编写上。在这段时间的学习中慢慢体会到设计模式是用来解决一类问题,而不是某些固定的代码片段。换句话说是解决问题的思想。设计模式可以解决模块的耦合关系,可以解决因需求变动带来的问题。程序在第一次编写时,各个模块之
39、间可能是紧耦合,但是经过代码重构,可以将模块之间变为松耦合。当然,我觉得我们也可以在软件设计之初把变化考虑到其中,对于业务型软件设计,在了解需求后,可以尽可能将其分出主次关系。也就是主体与枝节的关系。如下图对于工厂模式来说,要求高层模块变化相对较慢,底层模块变化相对较快。这样符合设计模式中的依赖倒置原则高层模块不依赖于底层模块。换句话说,软件设计是要划分易变部分和稳定部分。这样在一些枝节问题发生变化时,主干不用变化。如果是紧耦合状态,有可能造成一个地方变化,连带着很多地方要发生变化。工厂模式要解决的是“某个对象”的创建工作,由于需求的变化,这个对象常常面临着剧烈的变化,但是这个对象拥有的接口相
40、对稳定。也就是说:枝节常常发生变化,但是枝节与主干的接口相对稳定。设计模式中是这样说明:定义一个用于创建对象的接口,让子类决定实例化那个类。FactoryMethod使得一个类的实例化延迟到子类。现在看这句话可能有些不明白,我们一会再来分析一下。先来看看工厂模式的大体结构。如下图:我们还是用实例化汽车的例子来解释。对于客户端程序(ClientApp)如果想要一个汽车的对象,需要调用生产这个汽车的Factory的对象。当然,这个类继承自一个AbstractFactory基类。而这个Factory类就是设计模式中提到的“子类”,它来决定实例化那个类。下面我们来具体实现一下代码,首先,我们需要两个基
41、类,一个是Car的,一个是Factory的。Factory类型的作用是构建Car的对象。代码如下:public abstract class AbstractCarpublic abstract string Run();public abstract string Stop();public abstract string Accelerate();public abstract string Decelerate();public abstract class AbstractFactorypublic abstract AbstractCar CreateCar();下面,我们来做一个B
42、MW的实现代码:public class BMWCar:AbstractCarpublic override string Run()return BMW Run;public override string Stop()return BMW Stop;public override string Accelerate()return BMW Accelerate;public override string Decelerate()return BMW Decalerate;public class BMWFactory:AbstractFactorypublic override Abst
43、ractCar CreateCar()return new BMWCar();这样我们就可以在客户端程序得到一个BMW的实例,并使用它的方法:class Class1/ / 应用程序的主入口点。/ STAThreadstatic void Main(string args)AbstractCar car = CreateCarFunc(new BMWFactory ();Console.Write(car.Run() + n);Console.Write(car.Stop() + n);Console.Write(car.Accelerate() + n);Console.Write(car.Decelerate() + n);Console.Read();public static AbstractCar CreateCarFunc(AbstractFactory factory)return factory.CreateCar();在客户端程序中,我们AbstractFactory的对象来得到Car的对象结果如下:BMW RunBMW StopBMW AccelerateBMW Decalerate如果我们需求变了,现在要BORA的对象,那末,我们首先要对程序作