《大话设计模式》笔记
菜鸟遇到问题,只能用时间来摆平。而学设计模式的原因是通过好设计挣得时间。
不像head first和GOF等书让人根本读不下去。作为入门级别的DP书籍,算是很深入浅出了,相当值得一读,连敲代码,3天可以读完。 工厂模式:
工场模式是创建型模式,负责对象创建
增加新子类的时候,只需要修改对应工厂的switch,以及实现新子类,不会暴露其它的子类方法实现
例子:收银机:要支持两种收费策略,1是打折,2是返利(满100返30)
那么可以定义一个收费抽象类,各种收费策略实现抽象类(如原价,打折,返利),然后通过传入参数,返回不同收费类。
问题:假如又要打折,又要返利,该怎么办?
那么传入的可以是一个list<>,返回的也是一个list,在计算应收金额的时候顺序计算即可。
策略模式:
策略模式定义一些算法家族,分别封装起来,让它们之间可以互相替换
策略模式先定义支持算法的公共接口,具体的策略会封装具体的算法并通过接口实现。
关键点在于策略模式有一个上下文Context,通常用于存放具体的策略对象的引用
软件设计真正要做的内容,就是发现职责并将职责相互分离
面对需求,对程序的改动应是通过新增代码进行的,而不是更改现有代码
里氏代换原则:在软件里面,把父类都替换成子类,程序的行为不应产生变化
只有这样,父类才能够真正被复用
针对接口编程,可以倒转依赖。
WordlePF 应该使用这个模式去配置布局算法
装饰器模式:
可以动态给一个对象增加额外的职责
对于我来说,要开发一个avatar换装系统,我会将人物所有的衣服放到一个list里面,赋值给一个person类的finery成员
问题在于,finery这个东西其实可能只在某个场合会用到,加成员的做法会加重主类的负担
装饰器模式内,会不断传入已有的component,并为component新增其它元素,形成新的component
实际使用时会通过base.operation()来新增其它元素
代理模式:
为其它对象提供一个代理,以控制对这个对象的访问
代理类和实际被访问的类应该要有相同的接口
工厂方法模式:
简单工厂的最大优点,在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类。
但缺点却在于,新增类的时候,需要修改工厂类代码,违反了开放封闭原则,通过修改已有代码实现新功能。
工厂方法模式,则是定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到子类。
先定义一个工厂,当新增类时,就新增一个类继承工厂类。无需修改简单工厂类。但是缺点是客户端调用的时候,就需要知道所有类的信息了。
(工厂的作用在于:更换对象时,不需要做大的改动就可以实现)
原型模式:
原型模式通过Clone操作,将已构造的对象复制一次(避免了过多的构造计算,同时隐藏了对象构造细节)
对于所有非静态字段,值类型按位复制,引用类型复制引用,不复制引用的对象。
问题:浅复制不复制引用的对象。也就是说,对于原对象中的成员对象,Clone后的各个副本会共享。
那么为了实现深复制,就需要原有对象中的成员类,也实现ICloneable接口,在原对象的Clone操作中,也实现成员的Clone。
外观模式:
为子系统的一组接口提供一个一致的界面(中间层),此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
主要用于减少依赖。
建造者模式:
用于创建复杂的对象,其中对象的构建顺序是稳定的,但是内部的构建面临着复杂的变化。
观察者模式:
观察者模式又称发布/订阅模式。它定义了一个一对多关系,当一个通知者对象发生改变了以后,会立即调用多个观察者对象本身,让它们自动更新自己。(就像魔兽里面的游戏面板,英雄的血量会在多个地方显示,所以HP数值作为通知者,当改变了以后,自动通知头像面板,血条长度,血条颜色等)
抽象到更高的层次, 那就是将一个系统分割成一系列相互协作的类有一个很不好的副作用,就是需要维护相关对象的一致性。
但回到魔兽的例子里面,需要显示血量的地方都不一定是同一个类。因此通知者使用接口实现一个通知的委托,然后将各个观察者应做的操作添加到这个委托的eventHandler里面即可。
抽象工厂模式:
简单工厂和工厂方法可以创建不同的类。但不同的类的方法假如都比较类似,则可以使用抽象工厂模式
可以将不同的类的方法都由
另外,通过配置和反射的方法,就可以更好地实现配置级的抽象工厂了(即传入的参数
组合模式:
树形公司管理系统
当希望客户端可以忽略组合对象与单个对象的不同时,可以考虑使用组合模式
单例模式:(singleton)
很多场合,希望一个对象只实例化一次。而究竟有没有实例化过,应该由被实例化的对象,还是调用的过程去判断呢?
当然是被实例化的对象了
首先,要避免其它人对该对象构造函数的调用,就要将构造函数设置成private。然后在类中提供一个静态方法,这个方法会判断是否产生了一个对象。
游戏的面板,可以通过这些方式,只实例化一次。
多线程的情况下,利用lock语句和双重锁定方式,只在实例没有被创建的时候加锁,减少性能损耗。
桥接模式:(bridge)
一个很重要的设计原则——合成/聚合复用原则。优先使用合成,聚合,而不是继承。
因为继承是一种强耦合的结构,父类变,子类也必须要变。
一个软件的不同实现会有很多种,而桥接模式的核心意图是将实现独立,让它们各自变化,而不会影响其它实现。
命令模式:
将命令封装、
职责链模式: (chain of responsibility)
使多个对象都有可能处理请求,从而避免请求的发送者和接受者之间的耦合关系。对象会被连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
实际的设计中,具体处理这类会处理它负责的请求,也可访问它的后继者。假如不能处理该请求,就将请求转发给后继者。
这样的设计,职责分明,类的体积减少了,各个职责类之间也不知道其它职责类的效用。
而且,客户端也不知道究竟是谁决策了。
可用于批准报销、升职的OA系统管理场合。
中介者模式:
中介者模式需要解决的问题是:不同对象之间要对方的支持才能进行操作,都知道对方的话会形成大量的耦合。例如魔兽DOTA里面,买鸡以后要开共享,一个兵要去A另外一个兵,都有一些中介者模式的思想。即需要一个知悉所有对象信息的中介者,用于过程调用。
其实,函数作用域就是一个中介者。
中介者模式的好处是:引入新的对象时,不会造成大量的修改工作;对象之间如何协作,也被封闭了。
缺点:中介者的责任太大,维护的关系太复杂,任何的出错都会导致系统无法工作。
享元模式:
享元模式其实是单例模式的一种变体,但大量相同对象可能被创建的时候,只有未创建的内容会被创建,否则则指向已创建的对象的引用。
例如.net里面的string就是这样
string a = "abc";
string b = "abc";
Object.EqualReference(a,b) == true;
即两者指向的是同一个地方
但是,一个对象通常有一些可以共享的或不可以共享的内容。
例如,魔兽里面一个步兵,他的模型是可以共享的,这些称之为内部状态;血量,受到的光环影响,是移动还是在攻击等就不是可以共享的了,这些称之为外部状态。这样创建一队步兵的时候,模型这种耗内存的东西只生成一次就可以了。