有一天,有人也许运行这个代码并且注意到stack没有运行的如想象的那么快,并且能够在重负荷下使用。你能够重写stack,以至于它不用arraylist并且继续提高stack的效率。这是新的倾向的和有意义的版本:
class stack
{
private int stack_pointer = -1;
private object[] stack = new object[1000];
public void push( object article )
{
assert stack_pointer < stack.length;
stack[ ++stack_pointer ] = article;
}
public object pop()
{
assert stack_pointer >= 0;
return stack[ stack_pointer-- ];
}
public void push_many( object[] articles )
{
assert ( stack_pointer + articles.length )
注意到push_many不再多次调用push()―它做块传输。新的stack运行正常;事实上,比前一个版本更好。不幸的是,派生类monitorable_stack不再运行,因为如果push_many()被调用,它不正确的跟踪堆栈的使用(push()的派生类版本不再通过继承的push_many()方法调用,所以push_many()不再更新high_water_mark)。stack是一个脆弱的类。与关闭它一样,事实上不可能通过小心来消灭这些类型的错误。
注意如果你用接口继承,你就没有这个问题,因为你没有继承对你有害的函数。如果stack是接口,由simple_stack和monitorable_stack实现,那么代码就是更加健壮的。
我提供了一个基于接口的方法在listing 0.1。这个解决方法和继承实现的方法一样的灵活:你能够用stack抽象术语来写代码而不必担心你事实上在操作那种具体的堆栈。因为两个实现必须提供公共接口的所有东西,它很难使事情变糟。我仍然有和写基类的代码一样的只写一次,因为我用封装而不是继承。在底层,我不得不通过封装类中的琐碎的访问器方法来访问缺省的实现。(例如,monitorable_stack.push(…)(在41行)不得不调用在simple_stack等价的方法).程序员埋怨写所有这些行,但是写这特别行代码同消除重要的潜在bug是非常小的成本。
listing 0.1. 用接口消除脆弱基类
1 import java.util.*;
2
3 interface stack
4 {
5 void push( object o );
6 object pop();
7 void push_many( object[] source );
8 }
9
10 class simple_stack implements stack
11 { private int stack_pointer = -1;
12 private object[] stack = new object[1000];
13
14 public void push( object o )
15 { assert stack_pointer < stack.length;
16
17 stack[ ++stack_pointer ] = o;
18 }
19
20 public object pop()
21 { assert stack_pointer >= 0;
22
23 return stack[ stack_pointer-- ];
24 }
25
26 public void push_many( object[] source )
27 { assert (stack_pointer + source.length) < stack.length;
28
29 system.arraycopy(source,0,stack,stack_pointer+1,
source.length);
30 stack_pointer += source.length;
31 }
32 }
33
34
35 class monitorable_stack implements stack
36 {
37 private int high_water_mark = 0;
38 private int current_size;
39 simple_stack stack = new simple_stack();
40
41 public void push( object o )
42 { if( ++current_size > high_water_mark )
43 high_water_mark = current_size;
44 stack.push(o);
45 }
46
47 public object pop()
48 { --current_size;
49 return stack.pop();
50 }
51
52 public void push_many( object[] source )
53 {
54 if( current_size + source.length > high_water_mark )
55 high_water_mark = current_size + source.length;
56
57 stack.push_many( source );
58 }
59
60 public int maximum_size()
61 { return high_water_mark;
62 }
63 }
64
框架(frameworks)
没有提到基于框架编程,那使对于脆弱的基类的讨论是不完整的。诸如microsoft foundation classes(mfc)的基类已经成为建立类库的流行途径。尽管mfc本身正在神圣的隐退,但是mfc的基口已经是根深蒂固,而这无关于microsoft在那终止,程序员会一直认为microsoft的方法是最好的方法。
一个基于框架的系统典型的使用半成品的类的构成库开始,这些类不做任何需要做的事,而是依赖于派生类来提供需要的功能。在java中,一个好的例子就是组件的paint()方法,它是一个有效的占位者;一个派生类必须提供真正的版本。
你能够适度的多国一些东西,但是一个基于定制的派生类的完整的类框架是非常脆弱的。基类是太脆弱了。当我们用mfc编程时,每次microsoft公布新版本时,我不得不重写我的应用。这些代码将经常编译,但是由于一些基类的改变,它们不能运行。
所有提供的java包工作的非常好。为了使它们运行,你不需要扩展任何东西。这个已经提供的结构比派生类的框架结构更好。它容易维护和使用,并且如果sun microsystems提供的类改变了它的实现,也不会使你的代码处在危险中。
总结脆弱的基类
一般,最好避开具体基础类和extends关系,而用接口和implements关系。我的处理规则是,在我的至少80%的代码中完全用接口来完成。例如,我从不用对hashmap的引用;我用对map接口的引用。(我对interface这个字不是严格的。当你看怎样用接口的时候,inputstream是一个好的接口,尽管它在java中是作为抽象类来实现的。)
你增加的越抽象,就越灵活。在今天的商业环境下,需求随着程序开发而改变,灵活就是最主要的。而且灵敏编程中的大多数只有代码使用抽象来写才会很好的运行。
如果你近距离的检查四人帮的模式,你将看到这些模式中的很多是提供方法消除实现继承,而最好用接口继承,并且大多数模式的共有特征是用接口继承。这个重要事实是我们开始时提到的:模式是发现而不是发明。模式的出现是当你发现写得很好,易维护的运行代码时。它讲的是这些写得好的,易维护的代码根本的避开了实现继承。
这个文章是从我即将出版的书,暂时命名为《holub on patterns:learning design patterns by looking at code》,将有apress在今年秋季出版。
class stack
{
private int stack_pointer = -1;
private object[] stack = new object[1000];
public void push( object article )
{
assert stack_pointer < stack.length;
stack[ ++stack_pointer ] = article;
}
public object pop()
{
assert stack_pointer >= 0;
return stack[ stack_pointer-- ];
}
public void push_many( object[] articles )
{
assert ( stack_pointer + articles.length )
注意到push_many不再多次调用push()―它做块传输。新的stack运行正常;事实上,比前一个版本更好。不幸的是,派生类monitorable_stack不再运行,因为如果push_many()被调用,它不正确的跟踪堆栈的使用(push()的派生类版本不再通过继承的push_many()方法调用,所以push_many()不再更新high_water_mark)。stack是一个脆弱的类。与关闭它一样,事实上不可能通过小心来消灭这些类型的错误。
注意如果你用接口继承,你就没有这个问题,因为你没有继承对你有害的函数。如果stack是接口,由simple_stack和monitorable_stack实现,那么代码就是更加健壮的。
我提供了一个基于接口的方法在listing 0.1。这个解决方法和继承实现的方法一样的灵活:你能够用stack抽象术语来写代码而不必担心你事实上在操作那种具体的堆栈。因为两个实现必须提供公共接口的所有东西,它很难使事情变糟。我仍然有和写基类的代码一样的只写一次,因为我用封装而不是继承。在底层,我不得不通过封装类中的琐碎的访问器方法来访问缺省的实现。(例如,monitorable_stack.push(…)(在41行)不得不调用在simple_stack等价的方法).程序员埋怨写所有这些行,但是写这特别行代码同消除重要的潜在bug是非常小的成本。
listing 0.1. 用接口消除脆弱基类
1 import java.util.*;
2
3 interface stack
4 {
5 void push( object o );
6 object pop();
7 void push_many( object[] source );
8 }
9
10 class simple_stack implements stack
11 { private int stack_pointer = -1;
12 private object[] stack = new object[1000];
13
14 public void push( object o )
15 { assert stack_pointer < stack.length;
16
17 stack[ ++stack_pointer ] = o;
18 }
19
20 public object pop()
21 { assert stack_pointer >= 0;
22
23 return stack[ stack_pointer-- ];
24 }
25
26 public void push_many( object[] source )
27 { assert (stack_pointer + source.length) < stack.length;
28
29 system.arraycopy(source,0,stack,stack_pointer+1,
source.length);
30 stack_pointer += source.length;
31 }
32 }
33
34
35 class monitorable_stack implements stack
36 {
37 private int high_water_mark = 0;
38 private int current_size;
39 simple_stack stack = new simple_stack();
40
41 public void push( object o )
42 { if( ++current_size > high_water_mark )
43 high_water_mark = current_size;
44 stack.push(o);
45 }
46
47 public object pop()
48 { --current_size;
49 return stack.pop();
50 }
51
52 public void push_many( object[] source )
53 {
54 if( current_size + source.length > high_water_mark )
55 high_water_mark = current_size + source.length;
56
57 stack.push_many( source );
58 }
59
60 public int maximum_size()
61 { return high_water_mark;
62 }
63 }
64
框架(frameworks)
没有提到基于框架编程,那使对于脆弱的基类的讨论是不完整的。诸如microsoft foundation classes(mfc)的基类已经成为建立类库的流行途径。尽管mfc本身正在神圣的隐退,但是mfc的基口已经是根深蒂固,而这无关于microsoft在那终止,程序员会一直认为microsoft的方法是最好的方法。
一个基于框架的系统典型的使用半成品的类的构成库开始,这些类不做任何需要做的事,而是依赖于派生类来提供需要的功能。在java中,一个好的例子就是组件的paint()方法,它是一个有效的占位者;一个派生类必须提供真正的版本。
你能够适度的多国一些东西,但是一个基于定制的派生类的完整的类框架是非常脆弱的。基类是太脆弱了。当我们用mfc编程时,每次microsoft公布新版本时,我不得不重写我的应用。这些代码将经常编译,但是由于一些基类的改变,它们不能运行。
所有提供的java包工作的非常好。为了使它们运行,你不需要扩展任何东西。这个已经提供的结构比派生类的框架结构更好。它容易维护和使用,并且如果sun microsystems提供的类改变了它的实现,也不会使你的代码处在危险中。
总结脆弱的基类
一般,最好避开具体基础类和extends关系,而用接口和implements关系。我的处理规则是,在我的至少80%的代码中完全用接口来完成。例如,我从不用对hashmap的引用;我用对map接口的引用。(我对interface这个字不是严格的。当你看怎样用接口的时候,inputstream是一个好的接口,尽管它在java中是作为抽象类来实现的。)
你增加的越抽象,就越灵活。在今天的商业环境下,需求随着程序开发而改变,灵活就是最主要的。而且灵敏编程中的大多数只有代码使用抽象来写才会很好的运行。
如果你近距离的检查四人帮的模式,你将看到这些模式中的很多是提供方法消除实现继承,而最好用接口继承,并且大多数模式的共有特征是用接口继承。这个重要事实是我们开始时提到的:模式是发现而不是发明。模式的出现是当你发现写得很好,易维护的运行代码时。它讲的是这些写得好的,易维护的代码根本的避开了实现继承。
这个文章是从我即将出版的书,暂时命名为《holub on patterns:learning design patterns by looking at code》,将有apress在今年秋季出版。
闽公网安备 35060202000074号