这个自创练习源于Design Patterns in Ruby(Russ Olsen 2008),源代码只有Ruby的,我在Python又在Java中练习如何实现类似的结构,并由此加深对三种语言的了解
这个案例的内容是自动输出一个简短的报告,包括标题和内容。示例如下
<html>
<head>
<title>Monthly Report</title>
</head>
<body>
<p>Things are going</p>
<p>really, really well.</p>
</body>
</html>
another output:
***** Monthly Report *****
Things are going
really, really well.
策略包括输出HTML和平文本。
先看书上优化后的Ruby代码
class Report
attr_reader :title, :text
attr_accessor :formatter
def initialize(formatter)
@title="Monthly Report"
@text= ["Things are going","really, really well."]
@formatter= formatter
end
def output_report()
@formatter.output_report(self)#self is a reserved word
end
end
class HTMLFormatter
def output_report(context) #the actual parameter of content will be "self", which leads to initialized instance of Report that contains all instance variables
puts("<html>")
puts(" <head>")
puts(" <title>#{context.title}</title>")
puts(" </head>")
puts(" <body>")
context.text.each do |line|
puts(" <p>#{line}</p>")
end
puts(" </body>")
puts(" </html>")
end
end
class PlainTextFormatter
def output_report(context)
puts("***** #{context.title} *****")
context.text.each do |line|
puts(line)
end
end
end
report =Report.new(HTMLFormatter.new)
report.output_report
puts("\nanother output: ")
puts
report.formatter =PlainTextFormatter.new
report.output_report
假设大家(跟我一样)不是很了解Ruby,这个程序分成4部分:策略控制部分(Report)、具体策略部分(HTMLFormatter和PlainTextFormatter)以及主程序(最后的裸命令)。@title的@表示instance varibale,attr_accessor让改变量可在class之外获取并修改,reader则class外只读。、
刚才说这是优化之后的,意思是原来模仿Java等静态语言有一个抽象的类Formatter,两个策略都是继承它,但Ruby不推荐这样做,于是移除。
这个策略模式设计的核心是: def output_report()
@formatter.output_report(self) 。formatter在之前可变的“constructor”「report.formatter =PlainTextFormatter.new 这句对于静态语言来说比较神奇」里面已经设定,output_report(self)则自己呼叫自己。
Ruby暂时没有Flow这样的执行过程可视化工具,代码静态分析结果是:output_report从对象HTMLFormatter.new呼叫对象Report.new并传参数传递给它,然后Report.new呼叫HTMLFormatter.new里面的方法输出内容。可以看出函数作为参数传递的特点。
然后我试着在Python中模仿Ruby的句法写一个一样的东西:
class Report:
def __init__(self,formatter):
self.title='Monthly Report'
self.text= ['Things are going','really, really well.']
self.formatter=formatter
def output_report(self):
self.formatter.output_report(self)
class HTMLFormatter():
def output_report(context):
print('<html>\n')
print(' <head>\n')
print(' <title>', context.title, "</title>")
print(' </head>\n')
print(' <body>\n')
for line in range(len(context.text)):
print(' <p>', context.text[line], '</p>\n')
print(' </body>\n')
print(' </html>\n')
class PlainTextFormatter:
def output_report(context):
print("***** ",context.title," *****\n")
for line in context.text:
print(line,"\n")
report = Report(HTMLFormatter)
report.output_report()
print("\nanother output: \n")
report.formatter=PlainTextFormatter
report.output_report()
通过这个过程我学到了一些Python的句法,比如def __init__(self)
,以及神奇的self。
Python代码的重点也是方法作为参数传递,构造器的主要内容非常像。在数据抽象/Data Abstraction方面由于Python没有获取控制,且Python可获得性的基础是文件(同一个文件里可以有任意多类,彼此完全透明),所以Ruby里面accessor设置没有了。
程序运行流程应该和Ruby基本一致。
相比大家都非常了解Python,欢迎指正和评论。
最后出场的是Java。这个例子让我感觉到自己对Java的面向对象编程还是有很大学习空间的,在脑中(暂时还不会UML)构想类和之间关系时我参照优化前的Ruby代码,设置interface Formatter,但不考虑通过用把类作为参数传递来拙劣地模仿方法作为参数传递的手段,毕竟Java有它自己的特点。
Java代码分成四个文件:
package Report;
public interface Formatter {
public void outputReport(String title, String [] text);
}
package Report;
public class HTMLFormatter implements Formatter {
public void outputReport(String title, String [] text){
System.out.println("<html>");
System.out.println(" <head>");
System.out.println(" <title>"+ title +"</title>");
System.out.println(" </head>");
System.out.println(" <body>");
for (int i = 0; i <text.length ; i++) {
System.out.println(" <p>"+text[i]+"</p>");
}
System.out.println(" </body>");
System.out.println(" </html>");
}
}
package Report;
public class PlainTextFormatter implements Formatter {
public void outputReport(String title, String [] text){
System.out.println("*****" + title +" *****");
for (int i = 0; i <text.length ; i++) {
System.out.println(text[i]);
}
}
}
主程序
package Report;
public class GenerateReport {
private static String title= new String("Monthly Report");
private static String[] text={"Things are going","really, really well."};
public static void main(String[] args) {
HTMLFormatter report1 = new HTMLFormatter();
report1.outputReport(title,text);
System.out.println("\nanother output: \n");
PlainTextFormatter report2= new PlainTextFormatter();
report2.outputReport(title,text);
}
}
两个策略的设计与之前Ruby和Python的主要不同在于outputReport()的参数明确写明是title和text,主程序里以类数据(private static)的形式储存好数据,在创建策略对象后在输出命令调用时把数据传递给策略对象。
Java代码的实现除了我这种之外还有其他的,Java开发的一个特点是强调静态语言的动态行为,避免“把代码写死了”。我不会在main(String[] args)方面做文章,貌似这种处理方式也比较老旧了。