《大话设计模式》笔记

1.简单工厂模式

有多个子类的时候,可以用简单工厂模式。
就是用一个单独的工厂类,里面通过switch-case来实现不同子类的实例化

2.商场打折-策略模式

定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法客户
让策略和简单工厂结合,在CashContext里用工厂new不同的算法对象,这样客户端就只需要知道cashcontext就行了,后序要加新的算法也只需要改cashcontext。
而单独的工厂类,客户端要认识工厂类和算法类,这样耦合度就高了,改的地方也会变多。
优点:1.策略模式是一种定义系列算法的方法,从概念上看,这些算法完成的都是相同的工作,只是实现不同,它可以以相同的想法调用所有算法,减少各种算法类与使用算法类之间的耦合。
2.context析取出算法中的公共功能,如GetResult。
3.简化单元测试,因为每个算法都有自己的类,不会互相影响。
4.当不同的行为堆砌在一个类中的时候,必须要用条件语句选择合适的行为。但是将这些行为封装在一个独立的Strtegy类中,可以在使用这些行为的类中消除条件语句
5.在基本的策略模式中,选用具体的职责仍由客户端对象承担,然后转给Conetxt对象,但是策略模式和简单工厂结合后,选择具体实现的职责也可以由Context承担,减轻客户端职责。

3.拍摄UFO-单一职责原则

3.4单一职责原则

单一职责原则SRP:就一个类而言,应该仅有一个引起它变化的原因。
如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。(完全可以分出哪些是界面类,哪些是游戏逻辑类)
软件设计真正要做的许多内容,就是发现职责并把哪些职责相互分离,如果你能够想到多于一个动机取改变一个类,那么这个类就具有多于一个的职责,机应该考虑类的职责分离。

4.考研求职两不误——开放-封闭原则

开放-封闭原则:指软件实体(类、模块、函数等)应该可以扩展,但是不可修改。即对于扩展是开放的,对于更改是封闭的。
猜测最有可能发生的变化种类,然后构造抽象来隔离那些变化。比如加法直接在Client类实现,再实现减法又需要修改Client类,违反该原则,正确做法是一开始就增加一个抽象的运算类,只需要修改这个运算类的代码即可,上层调用它的Client不需要修改。
开放-封闭带来的好处:可维护、可扩展、可复用、灵活性好。应对频繁变化的那些部分做出抽象,但是要拒绝不成熟的抽象。

5.会修电脑不会修收音机?——依赖倒转原则

5.3依赖倒转原则

A.高层模块不应该依赖低层模块,两个都应该依赖抽象。
B.抽象不应该依赖细节。细节应该依赖抽象。

5.4里氏代换原则

子类型必须能够替换掉它们的父类型;如果鸟class会飞,那么企鹅class就不能继承鸟class;只有当子类可以替换掉父类,父类才能真正被复用,而子类也能够在父类的基础上增加新的行为,也是因为子类的课题环形才使得使用父类类型的模块在无需修改的情况下就可以扩展。
个人理解:如果鸟类继承自接口IFly,那么继承自鸟类的子类也需要会Fly,所以高层模块和低层模块都需要依赖接口或者抽象类。

6.穿什么有这么重要?——装饰模式

6.4装饰模式

装饰模式(Decorator):动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
装饰对象也继承自主类,并包含需要装饰的对象Component;每个装饰对象都需要将上一层的装饰对象设为自身的component,这样到最后一个装饰对象调用Operation函数时,会反向调用每个装饰对象的Operation函数;每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链当中。
好处:装饰模式有效地把类的核心职责和装饰功能区分开了,而且可以去除相关类中重复的装饰逻辑。
总结:系统需要新功能时,向旧类中添加新代码通常装饰了原有类的核心职责或主要行为,比如用西装来装饰person对象。但是主类加入新字段、方法等会导致主类复杂度增加,并且新加的东西只是为了满足特定情况才会执行的特殊行为。而装饰模式把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,当执行特殊行为时,可以有选择按顺序地使用装饰对象功能包装对象了。

7.为别人做嫁衣——代理模式

7.5代理模式

代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问。
实现方式:被代理类和代理类继承自同一个接口,举个栗子,均继承IRead接口。被代理类=代理类所代表的真正实体,所以Read函数就是真正的阅读功能;而代理类中要保存一个被代理类的引用对象使得代理可以访问实体,其中的Read函数就是调用被代理类的Read函数,而没有实际的功能。

7.6代理模式的应用

1.远程代理:为一个对象在不同的地址空间提供局部代表,这样可以隐藏一个对象存在于不同地址空间的事实。
2.虚拟代理:根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象。这样就可以达到性能最优,比如HTML网页很快打开,图片却还是在一张一张下,那些未打开的图片框就是通过虚拟代理来代替了真实的图片。如果直接用真实的图片类,那么网页就没那么快打开。
3.安全代理:用来控制真实对象访问时的权限。一般用于对象应该有不同的访问权限的时候。
4.智能指引:是指当调用真实的对象时,代理处理另外一些事。比如计算真实对象的引用次数,这样当该对象没有引用时,可以自动释放它。
代理模式本质就是就是在访问对象时引入了一定程度的间接性,因为这种间接性,可以附加多种用途。

8.雷锋依然在人间——工厂模式

8.4简单工厂vs工厂方法

简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。但是比如要创建新的运算类,那么工厂类一定会添加case的分支条件,就违背了开放-封闭原则。
工厂方法模式(Factory Method):定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类。比起上面的只需要添加新的运算类,并添加新运算类的生产工厂类就行了。
工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现运算类,类似简单工厂中的switch-case的判断问题还是存在,但是工厂方法把简单工厂的内部逻辑判断移到了客户端代码来实现。想要加的功能原本是改工厂类的,而现在是修改客户端。

9.简历复印——原型模式

9.3原型模式

原型模式(Prototype):用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。其实就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建细节。
不需要重新初始化对象,如果构造函数执行时间长,就可以提升性能,并且隐藏了对象创造的细节。

9.5浅复制与深复制

.NET在System命名空间中提供了ICloneable接口,其中唯一一个方法就Clone,只要实现这个接口就可以完成原型模式了。
MemberwiseClone()如果字段是值类型,就会逐位赋值,如果字段是引用类型,复制引用但不复制引用的对象,因此原始对象和副本引用同一对象。如果拷贝的类中用对象引用,那么后面修改这一字段时,原始对象和所有副本的这个字段都会变成最后一次修改的数据,因为引用的是同一对象。
解决方法:让这个引用对象类也实现ICloneabble接口,那么在原始对象被拷贝内也调用该引用对象的clone,就可以实现深拷贝。(如果该引用对象类内还有引用对象,那么就得嵌套实现ICloneable接口)

10.考题抄错会做也白搭——模板方法模式

10.4模板方式模式

模板方式模式:定义一个操作中的算法的股价,而将一些步骤延迟到子类中(配合继承、多态使用)。模板方法使得子类可以不改变一个算法的结构即可重定义该算法某些特定步骤。

10.5模板方法模式特点

通过把不变行为搬移到超类,去除子类中的重复代码来体现它的优势,也就是提供了一个代码复用平台。
当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。我们通过模板方法模式把这些行为搬移到单一的地方,这样帮助子类摆脱重复的不变行为的纠缠。

11.无熟人难办事——迪米特法则

迪米特法则(LoD),也叫最少知识原则:如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
也就是每个类都应当降低成员的访问权限,包装好自己的private状态,类之间的耦合越弱,越有利于福永,一个处在弱耦合的类被修改,不会对有关系的类造成波及。
举个栗子:IT部的a和b,a和b可以互不认识,由领导c来统筹调用,a走了,不影响b,而且你找IT部的人帮忙时也只需要认识领导c就行。

12.牛市股票还会亏钱——外观模式

12.4外观模式

外观模式(Facade),为子系统的一组接口提供一个一致的界面,此模式定义了一个高层接口,为了接口使得这一子系统更加容易使用。
举个栗子:基金类中包含多个不同股票类统一的买入卖出,客户端和基金类交互的时候并不需要知道该基金和哪几支股票打交道。

12.5何时使用外观模式

1.设计初期考虑在数据访问层和业务逻辑层、业务逻辑层和表示层的层与层之间建立外观Facade,这样可以为复杂的子系统提供一个简单的接口,使得耦合大大降低。
2.开发阶段,子系统因为不断重构变的很复杂,大多数模式使用也会产生很多很小的类,但是给外部调用的用户带来使用上的困难,增加外观Facade可以提供一个简单的接口,减少他们的依赖。
3.在维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展了,但是新的需求都要依赖其中的功能,可以开发一个外观Facade类,来提供设计粗糙或高度复杂的遗留代码的比较清晰接单的接口,让新系统和Facade对象交互,Facade与遗留代码交互所有复杂工作。

13.好菜每回味不同——建造者模式

13.4建造者模式

建造者模式(Builder),将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。(用户只需指定建造的类型就可以得到它们,而具体建造的过程和细节就不需要知道了)
建造者的Builder类里的建造方法必须要足够普遍,以便为各种类型的具体建造者构造。
Director类也是要配合继承、多态使用。

14.老板回来,我不知道——观察者模式

14.5观察者模式

观察者模式又叫发布-订阅(Publish/Subscribe)模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
为防止通知者和观察者类的代码耦合,两者均要分别继承自抽象通知者和抽象观察者类。这样在增加新的观察者类时不需要增加通知者类里的通知列表,不用违反开放-封闭原则。而且根据依赖倒转原则,我们也应该让程序都依赖抽象,而不是相互依赖。

14.6观察者模式特点

什么时候用观察者模式:当一个对象的改变需要同时改变其他对象的时候,并且它不知道具体有多少对象待改变时,应该考虑用观察者模式。
观察者模式所做的工作其实就是在接触耦合,让耦合的双方都依赖于抽象,而不是依赖于具体,从而使得各自的变化都不会影响到另一边的变化。

14.7观察者模式不足

尽管用了依赖倒转原则,但是抽象通知者还是依赖抽象观察者。1.万一没有抽象观察者这样的接口,通知的功能就完不成了。2.每个具体的观察者,收到通知后调用的方法可能根本不是同名方法,不能通过实现抽象观察者的Update来统一调用。

14.9事件委托说明

委托EventHnadler:无参数,无返回值。
委托就是一种引用方法的类型,一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值,委托可以看作是函数的抽象,是函数的“类”,委托的实例将代表一个具体的函数。一个委托可以搭载多个方法,所有方法被依次唤起,重要的是,他可以使得委托对象所搭载的方法并不需要属于同一个类。但是委托对象所搭载的所有方法必须具有相同的原型和形式,也就是有相同的参数列表和返回值列表。

所以可以让具体的观察者和通知者都无需继承自抽象,解决了本来与抽象观察者的耦合问题,只需要在具体通知者类中声明委托对象,并在客户端让委托搭载具体通知者的’Update’函数即可统一调用。

15.就不能不换DB吗?——抽象工厂模式

15.5抽象工厂模式

抽象工厂模式(Abstract Factory):提供一个创建一些列相关或互相依赖对象的接口,而无需指定它们具体的类。
IFactory是抽象工厂接口,里面应该包含所有的产品创建的抽象方法。为创建不同的产品对象,客户端应使用不同的具体工厂,用IFactory去实例化具体的工厂类,只需要在此处选择具体的工厂类,后面都不需要改动。

15.6抽象工厂模式的优缺点

优点:1.只需要改变具体工厂即可使用不同的产品配置,不能防止更改,只能确保改动最小,抽象工厂模式就只要改一次。2.具体创建实例过程和客户端分离,客户端通过接口操纵实例,产品的具体类名也被具体工厂的实例分离,不会出现在客户端代码中。
缺点:1.新增产品时要改动的类非常多。2.多次new工厂对象的话,要改具体生成的产品,就要改多处具体工厂类。

15.7简单工厂改进抽象工厂

就只有一个工厂类,根据工厂类的string变量配合switch-case来创建具体的产品类。
问题在于如果如果要新增一种工厂类,那么每种产品类的create方法的switch-case都需要根据新string写新分支,如果是抽象工厂只需要增加一个新的工厂类就行了。

15.8用反射+抽象工厂的数据访问程序

Assembly.Load(“程序集名称”).CreateInstance("命名空间.类名称)
仍然用抽象工厂,写多个具体类,只不过原本需要new 具体类()的方式来创建抽象工厂,那么需要改工厂类型就需要改每个new的地方。如果用反射,只需要用上面这行代码来代替new的地方,并且将"程序集名称"和"命名空间.类名称"用变量控制,那我们改工厂类时只需要改一个变量值即可。
原理:原本的实例化是写死在程序里的,准确的说是将程序由编译转为运行时,但是反射的CreateInstance(string)中的字符串可以写成变量。

16.无尽加班何时休?——状态模式

16.4方法过长是坏味道

面向对象涉及其实就是希望做到代码的责任分解,一个方法过长,而且有很多的判断分支就代表它的责任过大了。

16.5状态模式

状态模式(State):当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
状态模式主要是解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。
好处:将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。说白就是为了消除庞大的条件分支语句,状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少相互依赖。
何时用:当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式。

17.在NBA我需要翻译——适配器模式

17.2适配器模式

适配器模式(Adapter):将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
举个栗子:姚明刚去美国,立马学英语不现实,要和教练交流的话只能靠翻译,翻译就是适配器。
适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况。有类适配器模式和对象适配器模式。
客户端直接和适配器对象交互,而不用和被适配的类交互。(类似外观模式?).NET的DataAdapter类就是适配器模式的应用,用作DataSet和数据源之间的适配器,DataSet只需要和DataAdapter交互,而不需要知道是SQL Server还是Oracle的数据源。

18.如果再回到从前——备忘录模式

18.3备忘录模式

备忘录(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

18.4适用场景

Memento模式适用于功能比较复杂,但需要维护或记录属性历史的类,或者需要保存的属性只是众多属性中的一小部分时(可以不用把存储哪几个属性的细节暴露在客户端,而是把保存的细节Memento类封装起来)发起人类可以根据保存的Memento信息还原到前一状态。
如果需要存储的状态很大很多,备忘录对象会非常消耗内存。

19.分公司=一部门——组合模式

19.2组合模式

组合模式(Composite):将对象组合成树形结构以标识’部分-整体‘的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
Componet类:Leaf、Composite的接口类,定义Add、Remove、Display等接口函数。
Leaf类:叶节点,只能Display而不能Add和Remove。
Composite类:枝结点,实现Component的所有函数,且内部有List存子根结点和子叶节点,通过Add和Remove交互。

19.3透明方式与安全方式

透明方式:Component类中定义Add和Remove,Leaf结点也实现这两个函数,但是没有实际作用。好处是:客户端无需判断是叶结点or枝结点,直接调用即可。
安全方式:Component中不定义这两个函数,Leaf也就不可以调用,只在Composite类中定义,但是客户端调用前需要做相应的判断。

19.4何时用组合模式

需求中是体现部分与整体层级的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就考虑用组合模式了。
比如自定义控件:将一些基本控件组合起来写成一个定制控件,其实就是将这些单个控件add进根结点下组和起来,基本控件下面可能也是由其他更细的基本控件组成,但是每个子结点包括根结点都继承自一个基类,使得用户在使用基本控件or组合控件时没有感知,并不需要知道自己在用组合or基本的。
好处:基本对象可以组合成更复杂的组合对象,而这个组合对象又可以被组合,不断递归下去,任何用到基本对象的地方都可以用组合对象,而且用户不需要关心是处理一个叶结点还是一个组合组件,也不用为定义组合而写一些选择判断语句,让客户一致地使用组合结构和单个对象。

20.想走?可以!先买票——迭代器模式

20.2迭代器模式

迭代器模式(Iterator):提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。
当你需要访问一个聚集对象而且不管这些对象是什么都需要遍历时,就该考虑用迭代器模式。已经被foreach in代替,.NET中的迭代器其实只要实现MoveNext()和GetCurrent()就可以进行foreach遍历。
总的来说,迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样即可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。

21.有些类也需要计划生育——单例模式

21.4单例模式

单例模式(Singleton):保证一个类仅有一个实例,并提供一个访问它的全局访问点。
通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。只需要将构造函数写为private外部程序就不能用new来实例化它了,实例化交给类内部来实现。

21.5多线程时的单例

如果多个线程同时访问Singleton类调用GetInstance方法,会有可能造成创建多个实例。
需要给进程一把锁来处理,lick是确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。
但是每次调用都需要lock影响性能,先判断instance不为null再进行加锁处理。并且lock后需要再次判断instance是否为空,他因为如果有两个线程同时GetInstance并且通过第一重instancenull的判断,由于lock机制只有一个线程进入,另一个在外排队。如果第一个创建了实例,而且没有第二重instancenull的判断,第二个线程仍然可以创建新的实例,就没有达到单例的目的。

21.6静态初始化

1.将单例类用sealed修饰,防止派生,派生可能会增加实例。
2.将instance对象用readonly来修饰。private static readonly Singleton instance = new Singleton();
2意味着只有在静态初始化期间或者在类的构造函数中分配变量。由于这种静态初始化方式是在自己被加载时就实例化,所以被称为饿汉式单例。
原先的单例模式是要在第一次引用(getinstance)时才实例化,所以被称为懒汉式。
饿汉式:静态初始化的方式,类一加载就实例化的对象,要提前占用系统资源。
懒汉式:面临多线程访问时的安全问题,需要做双重锁定的处理保证安全。
从C#语言角度看,饿汉式单例已经够满足需求了。

22.手机软件何时统一——桥接模式

22.3合成/聚合原则

合成/聚合复用原则(CARP):尽量使用合成/聚合,尽量不要使用类继承。
翅膀和大雁是部分和整体的关系,并且它们的生命周期相同,所以大雁和翅膀是合成关系。强’拥有‘关系,严格的部分和整体关系,部分和整体的生命周期一样。
每只大雁属于一个雁群,一个雁群有多只大雁,所以大雁和雁群是聚合关系。聚合表示一种弱的‘拥有’关系,A可以包含B,但是B不是A的一部分。
好处:优先适用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上,这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。
继承是一种强耦合结构,父类变,子类必须变。

22.5桥接模式

桥接模式(Bridge):将抽象部分与它的实现部分分离,使它们都可以独立变化。并不是让抽象类与其派生类分离,实现指的是抽象类和它的派生类用来实现自己的对象。
比如手机软件基类和手机品牌基类是合成关系,手机品牌拥有手机软件,桥接模式就是把这两个类的实现独立出来,让他们格子变化,这就使得每种实现的变化不会影响其他实现。原本应该是手机软件(手机游戏)继承自手机品牌,就会有多重继承关系,这样增加一个新的手机软件类就会多写很多相似的子类。

23.烤羊肉串带来的思考——命令模式

23.6命令模式

命令模式(Command):将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

23.7命令模式作用

1.比较容易地设置一个命令队列。
2.在需要的情况下,可以较容易地将命令记入日志。
3.允许接受请求的一方决定是否要否决请求。
4.可以容易地实现堆请求的撤销和重做。
5.由于加进新的具体命令类不影响其他的类,因此增加新的具体命令类很容易。
6.命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分隔开。
如果不确定是否需要支持撤销/恢复操作功能,就不要使用命令模式,敏捷开发原则:不要为代码添加基于猜测的、实际不需要的功能。如果不清楚一个系统是否需要命令模式,一般就不要着急去实现它,事实上,在需要的时候通过重构实现这个模式并不困难,只有在真正需要如撤销/恢复操作等功能时,把原来的代码重构为命令模式才有意义。

24.加薪非要老总批?——职责链模式

24.3职责链模式

职责链模式(Chain of Responsibility):使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,知道有一个对象处理它为止。

24.4职责链的好处

最关键的是当客户端提交一个请求时,请求时沿链传递直至有一个ConcreteHnadler对象负责处理它。请求者不用管哪个对象来处理,反正该请求会被处理。
接收者和发送者都没有对方的明确信息,且链中的对象自己也并不知道链的结构。结果是职业链可简化对象的相互连接,它们仅需保持一个指向其后继者的引用,而不须保持它所有的候选接受者的引用,大大降低了耦合度。在客户端定义链的结构,也就是每个链结点的后继者。
每个请求都由职责链第一个结点发起,但是最后谁来决策,客户端不知道。

25.世界需要和平——中介者模式

25.1世界需要和平

尽管将一个系统分割成许多对象通常可以增加其可复用性,但是对象间相互连接的激增又会降低其可复用性。因为大量的连接使得一个对象不可能再没有其他对象的支持下工作,系统表现为一个不可分割的整体,所以对系统的行为进行任何较大的改动就十分困难了。迪米特法则说过两个类不用直接通信可以通过第三者转发这个调用,这个第三者就是中介者。

25.2中介者模式

中介者模式(Mediator):用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

25.4中介者模式优缺点

优点:减少各个类之间的耦合,自身的改变不会影响到其他类。其次,由于把对象如何协作进行了抽象,将中介作为一个独立的概念并将其封装在一个对象中,这样关注的对象就从对象各自本身的行为转移到它们之间的交互上,也就是站在一个更宏观的角度取看待系统。
缺点:把交互复杂性变成了中介者的复杂性,导致中介者比任何一个类都复杂。中介者模式很容易在系统中应用,也很容易在系统中误用。当系统出现了‘多对多’交互复杂的对象群时,不要急于使用中介者模式,而是先反思你的系统在设计上是不是不合理。
中介者模式一般应用与一组对象以定义良好但是复杂的方式进行通信的场合,以及想定制一个分布在多个类中的行为,而又不想生成太多子类的场合。

26.项目多也别傻做——享元模式

26.2享元模式

享元模式(Flyweight):运用共享技术有效地支持大量细粒度的对象。
包含享元工厂:用来创建并管理享元对象,主要是用来确保合理地共享享元对象,当用户请求一个享元对象时,工厂对象提供一个已创建的实例或者创建一个,工厂对象不需要提前生成对象实例,需要的时候判断是否为null再实例化也行。
享元超类;
享元类;
不需要共享的子类:尽管大部分时间都需要共享对象来降低内存的损耗,但个别时候也有可能不需要共享。

26.4内部状态与外部状态

享元模式可以避免大量非常相似类的开销。在程序设计中,有时需要生成大量细粒度的类实例来表示数据。如果能发现这些实例除了几个参数外基本都是相同的,有时就能够受大幅度地减少需要实例化的类的数量。如果能把那些参数移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享大幅度地减少单个实例的数目。

26.5享元模式的应用

如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;还有就是对象的大多数状态可以外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元模式。
因为用了享元模式,所以有了共享对象,实例总数就大大减少了,比如从字典取出dic[“零食”],所有零食都用这个共享对象,具体的零食名字、保质期这些就可以在用到的时候手动传入。如果共享的对象越多,存储节约也就越多,借阅量随着共享状态的增多而增大。
实际上.NET中,字符串string就是运用了享元模式,Object.ReferenceEquals(object a,object b),传入两个相同的字符串就会被判断为相同的实例。因为每次创建字符串对象时,都需要创建一个新的字符串对象的话,内存的开销会很大。如果第一次创建了字符串对象a"111",下次再创建相同的对象b只是把它的引用指向“111”,就实现了"111"在内存中的共享。
围棋游戏也可以考虑用享元模式,常规面向对象方式编程的话每盘棋都有几百个棋子对象产生,服务器的内存空间很难支持更多玩家玩了。

27.其实你不懂老板的心——解释器模式

27.2解释器模式

解释器模式(interpreter):给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
解释器模式需要解决的是,如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。context(信息)

27.3解释器模式好处

通常当一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。
解释器模式可以很容易地改变和扩展文法,因为该模式使用类来表示文法规则,可使用继承来改变或扩展该文法。也比较容易实现文法,因为定义抽象语法树中各个节点的类的实现大体类似,这些类都易于直接编写。
不足之处:解释器模式为文法中的每一条规则至少定义了一个类,因此包含许多规则的文法可能难以管理和维护。当文法非常复杂时,使用其他的奇数如语法分析程序or编译器生成器来处理。

28.男人和女人——访问者模式

28.5访问者模式

访问者模式(Vistor):表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变个元素的类的前提下定义作用于这些元素的新操作。
访问者模式的目的是把处理从数据结构分离出来。很多系统可以可以按照算法和数据结构分开,如果这样的系统有比较稳定的数据结构,又有已与变化的算法的话,使用访问者模式就是比较合适的,因为访问者模式使得算法操作的增加变得容易。
优点是增加洗的呢操作很容易,因为增加新的操作就意味着增加一个新的访问者。访问者模式将有关的行为集中到一个访问者对象中。
缺点是使增加新的数据结构变得困难了,除非数据结构不需要变化,不然一般用不到这个模式。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值