设计模式1.docx
- 文档编号:28551686
- 上传时间:2023-07-18
- 格式:DOCX
- 页数:27
- 大小:97.27KB
设计模式1.docx
《设计模式1.docx》由会员分享,可在线阅读,更多相关《设计模式1.docx(27页珍藏版)》请在冰豆网上搜索。
设计模式1
设计模式学习笔记
(一)——面向对象设计模式与原则
今天听了《C#面向对象设计模式纵横谈
(1):
面向对象设计模式与原则》课程。
总结了一些笔记。
首先介绍了什么是设计模式:
设计模式描述了软件设计过程中某一类常见问题的一般性的解决方案。
下面主要讨论面向对象设计模式。
面向对象设计模式描述了类与相互通信的对象之间的组织关系。
目的是应对变化、提高复用、减少改变。
那到底什么是对象:
1、从概念层面讲,对象是某种拥有职责的抽象;
2、从规格层面讲,对象是一系列可以被其他对象使用的公共接口;
3、从语言实现层面来看,对象封装了代码和数据(也就是行为和状态)。
对于这三点,我理解最深的应该是第三点。
这可能和我把大多精力放在了代码实现上有关,然而忽略了编程的的思想。
如果我们抛开代码的实现来看对象的概念,那么它应该就像一个具体的物体,比如说:
榔头,从概念层面讲,榔头有它的职责,也就是它是做什么用的(用来砸钉子,当然还会有其他用途,如防身),从规格层面讲,比如人使用榔头砸钉子。
面向对象的设计模式有三大原则:
1、这对接口编程,而不是针对实现编程。
在知道设计模式之前,我对接口的出现很不理解。
不明白为什么这个什么都不能实现的东西会存在,当然,也对多态表示茫然。
实际上我是还没有理解面向对象编程的思想。
在对设计模式略有了解后发现接口的确是一个好东西,用它实现多态的确减少了代码的修改。
比如说在《HeadFirstDesignPatterns》中有一个例子,说一个有关鸭子的游戏。
游戏当中有很多种的鸭子,如:
野鸭,木头鸭,鸭子模型。
我们首先会想到做一个抽象类:
abstractclassDuck,Duck当中有很多的抽象属性和方法,如quack。
我们用子类继承的时候都会实例化这个方法。
publicabstractclassDuck
{
publicabstractvoidquack()
}
publicclassMallardDuck:
Duck
{
publicoverridevoidquack()
{
Console.Write("Icanquack");
}
}
当程序成型后,我们有了很多种鸭子,突然,我们发现有的鸭子会飞,我们会在Duck中在加上一个抽象方法abstractvoidfly();于是我们不得不在所有的子类当中添加fly的实现,有人会说,如果我们在Duck中直接添加fly的实现,不久不用在子类中添加实现了吗?
那老板就该问你:
你见过木头鸭子满天飞(哦,天啊!
木头鸭子也飞起来了,这是什么世界!
)。
对不起老板,现在咱们都见到了。
这时我们换一种想法,如果我们把这些方法都提取出来,把它变成Duck的成员,好像问题就会简单些。
哈哈,好像扯的有点远了,现在回来接着记我的笔记。
2、优先使用对象组合,而不是类的继承。
这就使说多使用“hasa”,少使用“isa”,哈哈,我又想说回刚才的例子。
换个角度考虑Duck及其功能,我们设计一个fly的接口和一些具体的飞行方法。
publicinterfaceFlyBehavior
{
voidfly();
}
publicclassFlyWithWing:
FlyBehavior
{
publicvoidfly()
{
Console.Write("Icanfly\n");
}
}
publicclassFlyNoWay:
FlyBehavior
{
publicvoidfly()
{
Console.Write("Ican'tfly\n");
}
}
好了,对于Duck来说,现在它应该有一个(hasa)fly的方法
publicabstractclassDuck
{
publicDuck()
{}
publicFlyBehaviorflybehavior;
}
现在我们再来实现两种鸭子
publicclassModelDuck:
Duck
{
publicModelDuck()
{
flybehavior=newFlyNoWay();
}
}
publicclassMallardDuck:
Duck
{
publicMallardDuck()
{
flybehavior=newFlyWithWing();
}
}
这样如果要是在加上某种行为的话,我们就不必在每一种鸭子上下功夫。
把注意力放在我们关心的鸭子品种上(别太使劲关注,小心禽流感,“阿切!
”)。
3、封装变化点,实现松耦合,这点不用多说了。
课程中提到,编码当中的设计模式使用不是我们在编程之初就定下来的,应该是重构得到设计模式(RefactoringtoPatterns)。
哦,原来是这样,也好理解。
在编码中遇到问题,然后想想应对方式。
哈哈,我原来认为开始编程时就指定我们用什么设计模式呢。
下面说说设计原则:
1、单一职责原则(SRP):
一个类应仅有一个引起它变化的原因。
2、开放封闭原则(OCP):
类模块应可扩展,不可修改。
这里要说明一下,扩展和修改是不同的。
比如:
我们要在加一种ModelDuck,那么我们写一个ModelDuck的类继承Duck,这叫扩展,不是修改。
什么是修改,就好像我们开始说的那种作法,为了加一个fly的功能,我们要把所有的子类中加入不同的实现,这叫修改。
3、Liskov替换原则:
子类可替换基类。
4、依赖倒置原则:
高层模块不依赖于低层模块,二者都依赖于抽象。
还是刚才的例子:
Duck是一个高层模块,fly是低层模块。
Duck不依赖于fly,高层模块的改变慢,而低层模块的改变慢。
抽象不应依赖于实现细节,实现细节应依赖于抽象。
fly是一个抽象,它不依赖如何飞行。
5、接口隔离原则:
不强迫客户程序依赖于它们不用的方法(有道理,木头鸭子不会飞为什么要让它实现飞的功能。
)
最后有人提问接口和抽象类的区别:
接口可以多继承,抽象类只能但继承。
接口定义组件间的合同。
使用抽象类为一个isa的关系。
设计模式学习笔记
(二)——Singleton单件模式
这是我写模式设计的第二篇,首先来说说设计模式的分类。
基本的23种设计模式从目的上可分为三种:
1、创建型(Creational)模式:
负责对象创建。
2、结构型(Structural)模式:
处理类与对象间的组合,可以解决一些继承依赖性的问题
3、行为型(Behavioral)模式:
类与对象交互中的职责分配,可以解决组件间如何和交互,隔离变化。
下面来说说单件模式:
首先说说单件模式产生的动机,也就是为什么会出现单件模式。
有一些类在系统中只存在一个实例才能确保他们的逻辑正确性以及良好的效率。
这时我想到我遇到的一个问题。
我曾经遇到一个WinForm程序,运行后出现一个登陆框,输入用户名密码后点击登陆,然后显示一个登陆后的界面。
但是点击登陆后,程序要做一些操作,由于这段操作用时相对较长,在不经意时,我有点击了一次登陆按钮,最后出现了两个对话框。
如:
我现在有两个Form窗体Form1和Form2。
Form1上有一个按钮用来打开Form2并隐藏自己。
我们可以这样写:
privatevoidbutton1_Click(objectsender,System.EventArgse)
{
Form2form=newForm2();
form.Show();
this.Hide();
}
如果我们在显示Form2前由一些比较耗时的操作。
如:
我们让线程的沉睡10秒在显示Form2,当我们在线程沉睡时继续点击Form1上的Button,有可能就会出现两个Form2的窗体。
(我试过可以出现两个Form2,如果你有心试但没事出来别拿西红柿砍我,哈哈)
privatevoidbutton1_Click(objectsender,System.EventArgse)
{
Thread.Sleep(10000);
Form2form=newForm2();
form.Show();
this.Hide();
}
这种情况出现不能怪客户多点了一下,也不能说是编译器不够智能,应该是我们程序上的Bug,我想这种情况用单件模式应该可以解决。
单件模式的使用意图就是:
保证一个类仅有一个实例,并提供一个该实例全局的访问点(这句话当然不是我先说的,是引用Gof在《设计模式》中的一句话)
那类的设计者如何绕过常规的构造器来实现单件模式呢?
下面就来谈谈单件模式的实现。
单件模式在结构上使用了景泰方法来约束构造器(也就是构造函数)创建对象。
在单线程的情况下:
私有化构造函数,使类的使用者调用不到这个构造函数来new一个实例。
类型中可以自己new一个实例。
类中创建一个静态私有变量和Static公有属性。
在公有属性中实现此类的实例化。
这样在第一次请求时创建此对象。
代码如下:
classSingleton
{
privatestaticSingleton_instance;
privateSingleton(){}
publicstaticSingletonf_Instance
{
get
{
if(_instance==null)
{
_instance=newSingleton();
}
return_instance;
}
}
}
我在main函数中写入如下程序来查看一下这样写是否有效:
staticvoidMain(string[]args)
{
Singletont1=Singleton.f_Instance;
Singletont2=Singleton.f_Instance;
Console.Write(object.ReferenceEquals(t1,t2));
Console.Read();
}
控制台显示为True,开来还是有效的。
当然在Main中我也试过这样写:
Singletont1=newSingleton(),编译时告诉我Singleton()不可访问(当然,人家是私有的,不是自家人当然不见)
这种单线程下的单件模式有几点要注意:
1、构造器私有化(如果要此类被继承,可以用protected声明构造器)
2、不要支持IClinieable接口,因为会导致多个对象实例的出现
3、不能支持序列化
4、单件模式只考虑了对象创建的管理,没有考虑对象的销毁管理(创建自己的对象,销毁的事交给垃圾回收器吧)
5、不能应对多线程环境,因为会导致多个对象实例的出现
那在多线程下如何实现呢?
代码如下:
classSingletonMuli//多线程Singleton模式
{
privatestaticvolatileSingletonMuli_instance;//volatile是为了让编译器对此代码编译后的位置不进行调整
privateSingletonMuli(){}
privatestaticobjectlockHelper=newobject();//辅助器,不参与对象构建
publicstaticSingletonMulif_Instance
{
get
{
if(_instance==null)
{
lock(lockHelper)
{
if(_instance==null)//双检查
{
_instance=newSingletonMuli();
}
}
}
return_instance;
}
}
}
当然还有一些更简单的实现方法,如:
classSingleton1//可以用在多线程环境
{
publicstaticreadonlySingleton1_instance=newSingleton1();
privateSingleton1(){}
}
其中要提到的是在_instance私有字段的实例化叫做“内联初始化”。
内联初始化是指在声明时。
实际上面的代码上相当于如下代码:
PublicstaticreadonlySingleton1_instance;
StaticSingleton()//静态构造函数
{
_instance=newSingleton();//私有构造器
}
PrivateSingleton(){}
内联初始化时会先执行静态构造器,如果没有静态构造函数,系统会默认一个。
在访问此静态字段时执行静态构造器生成。
静态构造器保证了在多线程时只有一个线程执行,自动加锁。
当然,第二种实现方式也有一些缺点,如:
静态构造器必须是私有的、无参的。
不过也可以用其他的方式解决这类问题。
如可以用方法属性实现扩展或修改私有构造器。
现在我们可以回来看看我开始说的那两个Form的问题,我们现在可以这样实现:
privatestaticForm2form;
privatevoidbutton1_Click(objectsender,System.EventArgse)
{
Thread.Sleep(10000);
objectlockhelp=newobject();
if(form==null)
{
lock(lockhelp)
{
if(form==null)
{
form=newForm2();
form.Show();
}
}
}
this.Hide();
}
这样问题就解决了(我是没有点出来第二个Form2,如果那位点出来了,给我发Email,我请她/他在天津的烤鸭)
单件模式实际上是利用控制对象创造过程来控制对象的创造个数的方法,我们可以对其进行扩展,不是让他只生成一个对象,可以让他只生成几个对象,这样可以实现对象池。
单件模式的核心是:
如何控制用户使用new对一个类的实例构造器的任意调用。
设计模式学习笔记(三)——AbstractFactory抽象工厂模式
抽象工厂是一种创建型模式,是为了解决实例化时所带来的问题。
我们先来看看是什么问题,有的时候我们会遇到这种情况,我们需要一系列的对象。
举个例子,有一系列BMW汽车零部件的对象:
轮子bmwwheel,油箱bmwoilbox,在一个管理函数中调用它们,代码如下
classBMWWheel
{
publicBMWWheel(){};
}
classBMWOilbox
{
publicBMWOilbox(){};
}
publicvoidManage()
{
BMWOilboxoilbox=newBMWOilbox();
BMWWheelwheel=newBMWWheel();
}
如果现在需求变了,我们要用大众一汽BORA的零件,不用BMW的,那么我们除了要再加上相应的零件对象外还要将Manage函数中的对象更改为BORA的零件对象。
那这时发现new会带来了一些问题:
实现依赖,不能应对具体实例化类型的变化。
如何解决这类问题呢?
封装变化点。
(没有变化的就不需要封装)
工厂模式的缘起:
1、变化点在“对象创建”,因此就封装“对象创建”
2、面向接口编程
简单工厂问题:
1、不能应对“不同系列对象”的变化。
如:
我们要在上面的代码中加上其他的对象就不能很好的应对了
2、使用面向对象国内的技术来封装变化点
动机:
在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。
面对这种问题,我们想绕过常规的对象创建方法,提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合。
对于“紧耦合”,我原来是不喜欢这个词的,但是今天明白了,不是程序紧耦合不好,而是面对频繁变化的需求,紧耦合会使程序的编写变得很吃力。
如果面对一个不变的需求,松耦合和紧耦合在代码编写上应该是没什么区别的。
《设计模式》中解释这种模式的意图是:
提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定他们的具体类。
下面我们来看看如何使用抽象工厂模式完成对这种变化的封装:
首先我们的需求是BMW的车轮和油箱,当然他们要继承各自的基类,代码如下
abstractclassAbstractWheel
{
publicAbstractWheel()
{
//Console.Write("CreateaAbstractProduct");
}
}
abstractclassAbstractOilBox
{
publicAbstractOilBox()
{}
}
classBMWWheel:
AbstractWheel
{
publicBMWWheel()
{
Console.Write("CreateaBMWwheel");
}
}
classBMWOilBox:
AbstractOilBox
{
publicBMWOilBox()
{
Console.Write("CreateaBMWOilBox");
}
}
然后,我们在建立一个生产这些零件的工厂,它继承自一个抽象工厂
//抽象工厂
abstractclassAbstractFactory
{
abstractpublicAbstractWheelCreatWheel();
abstractpublicAbstractOilBoxCreatOilBox();
}
classBMWFactory:
AbstractFactory
{
publicoverrideAbstractWheelCreatWheel()
{
returnnewBMWWheel();
}
publicoverrideAbstractOilBoxCreatOilBox()
{
returnnewBMWOilBox();
}
}
现在我们在Main函数中调用它们:
staticvoidMain(string[]args)
{
AbstractFactoryfactory=null;
factory=newBMWFactory();
factory.CreatWheel();
Console.Write("\n");
factory.CreatOilBox();
Console.Write("\n");
Console.Read();
}
显示结果:
CreateaBMWwheel
CreateaBMWOilBox
现在我们想不用BMW的零件,用BORA的零件了,先写一些BORA零件的类:
classBORAWheel:
AbstractWheel
{
publicBORAWheel()
{
Console.Write("CreateaBORAWheel");
}
}
classBORAOilBox:
AbstractOilBox
{
publicBORAOilBox()
{
Console.Write("CreateaBORAOilBox");
}
}
然后我们再创建BORA零件的工厂:
classBORAFactory:
AbstractFactory
{
publicoverrideAbstractWheelCreatWheel()
{
returnnewBORAWheel();
}
publicoverrideAbstractOilBoxCreatOilBox()
{
returnnewBORAOilBox();
}
}
再来看看如何在Main函数中修改使其调用BORA的零件;我们只要在将Main中的factory对象实例化为BORA的工厂BORAFactory就可以了:
staticvoidMain(string[]args)
{
AbstractFactoryfactory=null;
factory=newBORAFactory();
factory.CreatWheel();
Console.Write("\n");
factory.CreatOilBox();
Console.Write("\n");
Console.Read();
}
结果如下:
CreateaBORAWheel
CreateaBORAOilBox
AbstractFactory模式的几个要点:
1、如果没有应对“多系列对象构建”的需求变化,则没有必要使用AbstractFactory模式。
2、“系列对象”指的是这项对象之间有相互依赖、或作用的关系。
3、AbstractFactory模式主要在于应对“新系列”的需求变动。
缺点是难以应对“新对象”的需求变动。
这一点应该注意,就像前面说的,如果我们现在要在加入其他系列的类,代码的改动会很大。
4、AbstractFactory模式经常和FactoryMethod模式共同组合来应对“对象创建”的需求变化。
设计模式学习笔记(四)——Builder生成器模式
Builder生成器模式是一种创建型模式,它主要是应对项目中一些复杂对象的创建工作。
所谓“复杂对象”,是只:
此对象中还含有其它的子对象。
Builder模式所面对的情况是:
各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将他们组合在一起的算法却相对稳定。
简单的说:
子对象变化较频繁,对算法相对稳定。
这是解决一个复杂对象的创建工作,现在变化的部分和相对稳定的部分已经明确,我们要做的是隔离变化,如何将子对象和算法隔离是要解决的问题。
《设计模式》中说道:
将一个复杂对象的构建与其表示向分离,使得同样的构建过程可以创建不同的表示。
我们现在定义一个场景:
还是选择汽车,BMW和BORA。
试想一下,如果我们用较为普通的写法可以写成如下代码:
publicstaticvoidMain()
{
Carcar=newCar();
Console.Write("Wheel:
"+car._wheel+"\n");
Console.Write("OilBox:
"+car._oilBox+"\n");
Console.Write("Body:
"+car._body+"\n");
Console.Read();
}
publicclassCar
{
publicstring_wheel=“BMWWheel”;
publicstring_oilbox=“BMWOilBox”;
publicstring_body=“BMWBody”;
}
当我们不确定或因需求变化而改变对
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 设计 模式