设计模式零散知识笔记

关注执行及描述任务

1、在很多情况下,我们需要定义自己的语言。比如让管理员自己设置一个检查用户输入值的时候,可以有“等于”、“大于”、“小于”等中文操作符(当然这仅仅是个比较2的例子)。那么我们就需要用到解释器模式了。我们知道操作符的运算结果也是某一种类型数值,所以这里就用到了之前学到的组合模式,让“值类型”直接继承基类,让“操作符类型”继承一个次基类,该次基类继承基类并自定义一个抽象函数“操作函数”,以让“等于”、“大于”的类能在里面实现自己的功能。当然了,对于一个编程语言而言,“环境”是个十分重要的甚至应该说是基础的东西,因为所有的键-值都存储在里面,所以这里就要定义一个环境类,用来存储键-值对。当然了,解释器模型只是关注于如何实现各个“值类”和“操作类”,而不涉及解析(词法、语法解析等),要看语言解析的粗略代码,请看《深入PHP对象、模式与实践》的附录B吧。另外强调一点,解释器模式因为给每个操作符和值类型定义一个类,所以如果语言复杂的话类的数量会迅速膨胀,所以只适用于创建一些简单的语言。(2013-4-17)

2、如果一个类中的某个方法需要根据环境而变化,那么就需要将该方法(或同一情景下的多个方法)封装为一个子类,尤其继承树中多个类都要实现该方法时。为什么这么做?打个比方,食物A有“煎着吃”、“烧着吃”、“烤着吃”,食物B也有“煎着吃”、“烧着吃”、“烤着吃”、食物C……如果食物种类越多则越麻烦,而且会有大量的代码重复。此时在食物的基类中保存一个“吃”的方法类,只在创建对象时传递具体的“吃”的方法类即可。这样所有的食物类只需调用“吃”这个方法,具体怎么吃?看你的心情,你今天想怎么吃就给什么样的“吃类”即可。其实中有点像“原型模式”中一个类封装多个相关的类的祖先类,在调用时允许“A牌轮胎”和“B牌方向盘”组合成汽车一样的道理。只不过不同的是,“原型类”关注的是各个“组件类”的组合模式,而这里的模式(策略模式)关注的是具体的执行方法类的组合模式。利用此模式也可以方便的添加“吃”的类型,而不用修改食物类(2013-4-18)

3、策略模式关注的是已经确定有某个方法但具体实现不确定的情况。那么有木有一种模式,关注的是确定有某个类,但类中的方法数目和方法组合不确定的情况(比如A类有时只有A方法,有时有B方法,有时却有B、C、A方法。)?好吧,既然你提出来了,那就如你所愿(:P),我们的观察者模式闪亮登场。怎么实现呢?比如这里有一个Login类,里面包含了简单的账号密码检测,然后该类里有一个存储“方法类”的数组func_array,一个“注册函数”,一个“消除函数”。啥用呢?简单的说,就是在某种情况下,你需要在登陆后的同时执行A方法的类,就把A方法注册到Login类中(存储到func_array),还需要C方法和D方法,一起注册进去。此时如果有用户登陆,遍历func_array,依次执行A、C、D方法。当然啦,A、C、D方法也能根据情况(Login的状态,就是把Login类的引用也传递给方法)实现不同的操作。没错,这就是让很多人为之激动的观察者模式,PHP官方甚至为此专门提供了类似func_array(当然,比数组高端的多的操作类,但功能差不多)的类SplObjectStorage,还有一个接口,可以让类似Login的类实现此接口,可完成对SplObjectStorage的操作,以及一个所有可注册的方法类都必须实现的接口SplSubject。具体使用方法查看http://cn2.php.net/spl。(2013-4-18)

4、 在一个组合类的继承树中,经常有每个类必须实现但每个类的具体实现又都不同的时候,需要在每个类中添加对应的一个函数。如果经常性的需要添加新的动作,则会导致类不断膨胀。有没有一种方法能够将类中的方法(个人认为最好是分离那些不直接利用私有数据的方法)分离出来?好了,这就是我们的访问者模式,它主要利用在数据结构基本稳定,而操作的算法经常变动的环境。每个访问者类仅带有一个特定的功能,但对继承树中所有的类都实现该功能。比如继承树中有“面包”、“米饭”类,访问者类带有的特定功能是“cook”,则一般该访问者类名取为“cookVisitor”,其内部函数则始终取名为“visit面包”和“visit米饭”,这都是该访问者类所必须实现的,针对类的不同实现方式不同。而在组合类(如上述的面包)的实现accept函数(用于接收访问者cookVisitor),accept函数内实现“visit本类名”,如“visit面包”。访问者模式的取名相当不错且接近现实,比如我们的家是一个类结构,访问者是收税员,则由收税员告诉我们如何税收的具体数目和操作,同样如果是收水费员,则具体操作也由其告知我们。这样是不是比较好理解了^_^。PS:当然看了之前3的人有可能会想观察者模式不是也能把具体方法分离出来么,但是观察者模式更多的是倾向于事件触发机制的,其函数基本都是相对于具体事件而不是针对不同类来进行设计的。(2013-4-22)

5、很多情况下,我们需要在多个页面里有登陆模块、留言模块等等功能,难道我们要在各个页面的PHP页面里实现相同的代码?显然,这是一个糟糕的实践。那么我们有木有办法能把各个功能模块化,在任何时候需要时调用该模块即可?嗯,这就是我们的命令模式,那么为什么叫命令模式呢?Dos下的黑框框其实就是个很好的说明,所有功能都模块化后存在好,无论需要使用哪个功能都只要在黑框框里敲入指定的命令即可。至于输入的命令(web里一般地址或者地址的某一部分)是怎么找到对应的命令执行模块呢?这里就用到了命令工厂commandFactory,接受到指定命令后,转为为指定目录下的具体php文件名(具体实现可不同,只要遵循指定的规则即可)。比如action为login(或“other”)的传递给commandFactory,然后commandFactory找到“./command/loginAction.php(或“otherAction”)”下的Login(Other)类实例化后并返回即可。然而命令的执行通常需要知道特定的环境context,比如用户名username和密码pass就必须传递给它,所以loginAction通常接收一个环境变量context,那么这个context是谁传递给它的呢?这就需要控制者类controller出场了,它有有个context(理解为存储变量的仓库)的属性,将必要的变量存储进去后,再利用commandFactory找到对应的命令类,然后执行execute即可(通常只有这么一个对外的函数,这就要求指定的comman足够细化并足够独立,因为它也只有一个对外的execute函数。如果一个command类的execute函数功能太多,就要考虑是否进行拆分)。(2013-4-22)


关注让面向对象编程更加灵活

组合可比继承提供更多的灵活性。
组合模式:可像对待单个对象那样对待组合对象
1、定义了一个单根继承体系,容器对象(如军队)和他们包含的对象(也叫叶子类,如士兵)共享同一个接口。当然这可能导致叶子类中要实现它并不需要的接口,比如添加士兵,这是在叶子类中为组合模式中基类的透明性所作出的代价。这里的解决办法是在基类中加一个识别当前类是否是叶子类的方法getThisType(若是则返回null),然后建立一个组合类的继承自基类的“次基类”,该“次基类”覆盖基类的getThisType方法并返回当前对象的类(区别于叶子类的返回值null),客户端则根据是否为null决定是否进行“添加士兵”等组合类才有的操作。组合模式的模式的缺陷之一就是它依赖于其组成部分的简单性,当我们引入复杂的规则会导致模型越来越复杂而难以维护。所以在大部分叶子对象可以互换的情况下,组合模式才最适用。另外组合模式的成本也需要经常被考虑,因为它是逐层调用包含的对象的方法。所以在父对象中保存缓存值能提高效率,但要因此设计移除缓存的策略,这可能会要求你给子对象加上对父对象的引用。组合模式难以使数据保存在关系数据库中,但却非常适合持久化保存在XML中,因为XML元素通常也是由树形结构的子元素组合而成。(2013-4-16)

2、承接组合模式话题,如果我们需要定义食物对象,简单的想,因为食物基本都有“被吃”的功能,所以这里决定用组合模式。假定有“苹果”、“蛋糕”种类,而根据新鲜度不同,食物的作用也不同。所以如果我们需要“新鲜”、“腐败”等来定义食物的话,为此我们不得不新建“新鲜的苹果”、“腐败的苹果”、“新鲜的蛋糕”、“腐败的蛋糕”…等等,这还好,可是当修饰词越来越多、食物种类也越来越多时,这就使得食物类过于繁多。所以,为了解决组合模式中的这个问题,我们有了装饰模式。其实装饰模式很简单,所有装饰模式的累有一个“次基类”,为什么叫“次基类”,因为它继承自“食物的基类”。这个次基类保存了一个“食物的基类”,然后装饰模式继承它后,能够接受一个食物类(注意区别于装饰类),然后调用方法时对返回的结果进行适当的修饰即可。比如食物类能够填饱%80,而腐败的食物能够填饱%8(这里调用食物类的返回值并*0.1即可,另外暂时不考虑吃坏肚子的问题:P)。可以看到,只要添加一个”新鲜的“修饰类,就可以接受”苹果“或者”蛋糕“以实现”新鲜的苹果“和”新鲜的蛋糕“。此外,像是类之间彼此还能够叠加,比如可以有新鲜的海南产的苹果。这里只要再新建一个“海南的”修饰类即可有木有发现超级好用啊!?哈哈哈哈(2013-4-16)

3、外观模式,这个模式给我不是很有感觉。目的是为了在我们调用子系统的函数时,避免直接调用各个子系统内部较深入的函数从而过于耦合,而是将子系统下的函数进行组合并封装为一些定义明确的接口,从而让主系统调用时更加简洁明了。简单的说,就是创建一个类,定义封装好子系统的功能的函数(这样可以隐藏一些内部函数调用时的细节问题)供主系统调用即可。(2013-4-16)


关注生成对象

1、针对接口编程而不是针对实现编程。利用面向对象的特性,鼓励使用超类,尽可能用接口(延迟实现)代替if-else语句,能提高可扩展性和代码简洁性。(2013-4-12)

2、实例化对象是一件麻烦的事情,尽量用“工厂”来做对象的实例化,即将实例化的工作委托给具体的类或者方法。这就有了工厂模式。通常“创建者”和“产品”都是一一对应的,而且“创建者”和“产品”都有一个基类。比如说“创建者”是汽车生产商,子类继承后成为的“A牌子汽车的生产商”、“B牌子汽车的生产商”。对应的,“产品”就有了“A牌汽车”和“B牌汽车”。当然了,在产品有很多并且产品之间有关联的情况下,为每个产品创建一个创建者类显然有点吃力。这就有了抽象工厂模式,其实和工厂模式差不多,只是抽象工厂模式管理着一系列的相关的产品。比如“A牌子汽车的生产商”可以对应着“A牌子汽车的轮胎”、“A牌子汽车的发动机”、“A牌子汽车的座位”等等。什么?你想用“A牌子汽车的轮胎”和“B牌子汽车的发动机”来组装?OK,没有问题,这就是我们的原型模式。它实现一个工厂类,里面包含了所有产品的基类,构造函数接收具体的产品类型,比如将以上的“A牌子汽车的轮胎”和“B牌子汽车的发动机”赋予它,那么这个工厂实例产出的汽车就是你所赋予的。这给了我们通过组合的形式实现更丰富产品的自由。当原型工厂生产产品时,是用__clone来深度复制构造函数赋予的产品来返回。(2013-4-12)

3、全局变量容易引发bug,把类捆绑在了特定环境,破坏了封装,且不同类库的全局变量容易冲突(更糟糕的是PHP不警告此类问题)。并且全局变量本身不被保护的本质就是个极大的问题。而单例模式可以非常优雅的解决这个问题,它定义一个类,且只能产生一个对象。它保证①全局对象可以被系统的任何对象使用。②存储在不会被覆写的对象中(除非调用全局类自身的方法)。③对象唯一。   PS:当然了,单例对象和全局变量一样也可能被误用,不过适度使用还是能改进系统的。(2013-4-12)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值