分享
 
 
 

翻译TIPatterns--算法分解(Algorithmic partitioning)

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

算法分解(Algorithmic partitioning)

命令模式(Command):运行时刻选择操作

在《Advanced C++: Programming Styles And Idioms》 (Addison-Wesley, 1992) 一书中,Jim Copline借用了functor这个术语,用以指代那些只为封装一个函数而构造的对象(因为“functor”在数学上有专门的含义,所以在本书中我会使用更明确的术语:函数对象function object)。关于这个模式重要的一点是,将被调用函数的选择和被调用函数的调用分离开来。

《设计模式》只是提到函数对象这个术语而没有使用它。但是,关于函数对象的话题在那本书的模式章节里倒是重复了好几次。

从本质上说,Command就是一个函数对象:一个被封装成对象的方法。通过把方法封装到一个对象,你可以把它当作参数传给其它方法或者对象,让它们在实现你的某个请求(request)的时候完成一些特殊的操作。你可能会说Command其实就是一个把数据成员换成行为的messenger(因为它的目的和使用方法都很直接)。

//: command:CommandPattern.java

package command;

import java.util.*;

import junit.framework.*;

interface Command {

void execute();

}

class Hello implements Command {

public void execute() {

System.out.print("Hello ");

}

}

class World implements Command {

public void execute() {

System.out.print("World! ");

}

}

class IAm implements Command {

public void execute() {

System.out.print("I'm the command pattern!");

}

}

// An object that holds commands:

class Macro {

private List commands = new ArrayList();

public void add(Command c) { commands.add(c); }

public void run() {

Iterator it = commands.iterator();

while(it.hasNext())

((Command)it.next()).execute();

}

}

public class CommandPattern extends TestCase {

Macro macro = new Macro();

public void test() {

macro.add(new Hello());

macro.add(new World());

macro.add(new IAm());

macro.run();

}

public static void main(String args[]) {

junit.textui.TestRunner.run(CommandPattern.class);

}

} ///:~

Command模式最重要的一点就是,你可以通过它把想要完成的动作(action)交给一个方法或者对象。上面的例子提供了一种方法把一系列的动作排队后集中处理。这种情况下,你可以动态的创建新的行为,而通常你只能靠编写新的代码才能做到这一点;上例中,你可以通过解释脚本来完成(如果你需要(动态改变)的行为非常复杂,那么请参看Interpreter模式)。

另外一个关于Command模式的例子是refactor:DirList.java[????]。DirFilter类是一个command对象,它的动作包含在accept()方法里,而这些动作又需要传递给list()方法。List()方法通过调用accept()来决定结果中需要包含哪些东西。

《设计模式》一书写道“Commands其实就是回调函数(callbacks)的面向对象替代品(replacement)。”但是,我认为,“回(back)”对于回调(callback)的概念来说是非常关键的。也就是说,回调函数实际上会回溯到它的创建者。而另一方面,通常你只是创建Command对象然后把它传递给某个方法或对象,随后创建者和Command对象之间不再有什么联系。总之,这是我个人的一点看法。本书后面的章节,我会把一组相关的设计模式放到一起,标题是“回调”。

Strategy模式看起来像是从同一个基类继承而来的一系列Command类。但是仔细看看Command模式,你就会发现它也具有同样的结构:一系列分层次的函数对象。不同之处在于,这些函数对象的用法和Strategy模式不同。就像前面Refactor: DirList.Java那个例子,使用Command是为了解决特定问题 ——从一个列表选择文件。“不变的部分”是被调用的那个方法,变化的部分被分离出来放到函数对象里。我想冒昧的下个结论:Command模式在编码阶段提供灵活性,而Strategy模式的灵活性在运行时体现出来。尽管如此,这种区别却是非常模糊的。

练习:

4.用Command模式重做第三章的练习1。

职责链(Chain of responsibility)

职责链模式可以被想象成递归(recursion)的动态泛化(generalization),这种泛化通过使用Strategy对象来完成。被调用的时候,链表里的每个Strategy对象都试图去满足这次调用。当某个策略(strategy)调用成功或者整个strategy链到达末尾的时候,这个过程结束。递归的时候,某个方法不断的调用它自己直到满足某个结束条件;对于职责链,某个方法调用它自己,进而(通过遍历strategy链表)调用它自己的不同实现,知道某个满足某个结束条件。所谓结束条件,要么是到达链表的末尾,要么是某一个strategy调用成功。对于第一种情况,得返回一个默认对象;如果不能提供一个默认结果,那就必须以某种方式告知调用者链表访问成功与否。

由于strategy链表中可能会有多于一个的方法满足要求,所以职责链似乎有点专家系统的味道。由于这一系列Strategy实际上是一个链表,它可以被动态创建,所以你也可以把职责链想象成更一般化的,动态构建的switch语句。

在GoF的书中,有很多关于如何把职责链创建成链表的讨论。但是,仔细看看这个模式你就会发现如何管理链表实际上并不重要;那只是一个实现上的细节。因为GoF那本书是在标准模板库(STL)被大多数C++编译器支持之前写的,他们之所以要讨论链表的原因是(1)没有现成的链表类,所以他们得自己写一个(2)学术界常常将数据结构作为一项基本的技能来讲授,而且GoF那时候可能还没意识到数据结构应该成为编程语言的标准工具。我的主张是,把职责链作为链表来实现对于问题的解决没有任何裨益,它可以简单的通过使用标准的java List来实现,下面的例子会说明这一点。更进一步,你会看到我还费了些劲把链表管理的部分从不同Strategy的实现里分离出来了,从而使这部分代码更易于重用。

前面章节StategyPattern.java那个例子里,很可能你需要的是自动找到一个解决问题的方案。职责链通过另外一种方法达到这种效果,它把一系列Strategy对象放到链表里,并提供一种机制让它自动遍历链表的每一个节点。

//: chainofresponsibility:FindMinima.java

package chainofresponsibility;

import com.bruceeckel.util.*; // Arrays2.toString()

import junit.framework.*;

// Carries the result data and

// whether the strategy was successful:

class LineData {

public double[] data;

public LineData(double[] data) { this.data = data; }

private boolean succeeded;

public boolean isSuccessful() { return succeeded; }

public void setSuccessful(boolean b) { succeeded = b; }

}

interface Strategy {

LineData strategy(LineData m);

}

class LeastSquares implements Strategy {

public LineData strategy(LineData m) {

System.out.println("Trying LeastSquares algorithm");

LineData ld = (LineData)m;

// [ Actual test/calculation here ]

LineData r = new LineData(

new double[] { 1.1, 2.2 }); // Dummy data

r.setSuccessful(false);

return r;

}

}

class NewtonsMethod implements Strategy {

public LineData strategy(LineData m) {

System.out.println("Trying NewtonsMethod algorithm");

LineData ld = (LineData)m;

// [ Actual test/calculation here ]

LineData r = new LineData(

new double[] { 3.3, 4.4 }); // Dummy data

r.setSuccessful(false);

return r;

}

}

class Bisection implements Strategy {

public LineData strategy(LineData m) {

System.out.println("Trying Bisection algorithm");

LineData ld = (LineData)m;

// [ Actual test/calculation here ]

LineData r = new LineData(

new double[] { 5.5, 6.6 }); // Dummy data

r.setSuccessful(true);

return r;

}

}

class ConjugateGradient implements Strategy {

public LineData strategy(LineData m) {

System.out.println(

"Trying ConjugateGradient algorithm");

LineData ld = (LineData)m;

// [ Actual test/calculation here ]

LineData r = new LineData(

new double[] { 5.5, 6.6 }); // Dummy data

r.setSuccessful(true);

return r;

}

}

class MinimaFinder {

private static Strategy[] solutions = {

new LeastSquares(),

new NewtonsMethod(),

new Bisection(),

new ConjugateGradient(),

};

public static LineData solve(LineData line) {

LineData r = line;

for(int i = 0; i < solutions.length; i++) {

r = solutions[i].strategy(r);

if(r.isSuccessful())

return r;

}

throw new RuntimeException("unsolved: " + line);

}

}

public class FindMinima extends TestCase {

LineData line = new LineData(new double[]{

1.0, 2.0, 1.0, 2.0, -1.0, 3.0, 4.0, 5.0, 4.0

});

public void test() {

System.out.println(Arrays2.toString(

((LineData)MinimaFinder.solve(line)).data));

}

public static void main(String args[]) {

junit.textui.TestRunner.run(FindMinima.class);

}

} ///:~

练习:

1. 用职责链写一个专家系统,它一个接一个的尝试不同的解决方法,直到找到某个解决问题的方法为止。要求专家系统可以动态的添加解决方法。测试方法只要用字符串匹配就可以了,但是当匹配以后专家系统必须返回适当类型的ProblemSolver对象。这里还会用到什么其它的模式?

2. 用职责链写一个语言翻译系统,程序先访问本地的一个专用翻译系统(它可能可以针对你的问题所属的领域提供一些细节),然后访问一个更综合的通用翻译系统,如果不能完全翻译的话,最后访问BabelFish。注意:每种翻译系统都会尽可能翻译它们能够翻的那部分。

3. 用职责链写一个重新格式化java源代码的工具,它通过尝试不同的方法来分行。注意,普通代码和注释可能需要区别对待,这可能需要实现一个职责树Tree of Responsibility. 还需注意这种方法和Composite模式之间的相似性;或许这种技术更一般的描述应该叫做:组合策略(Composite of Strategies)。

目录

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有