经过了一个周末足不出户的草稿与总结,终于能记点东西了。这是第一次,一个人写,经过认真思索的东西,因为之前,我发现,我一看这家伙--主题,我会几天就把自己看的东西忘了个精光,实话说,我很认真的,因为有牛人说,依赖程序员的记性,这是非常 糟糕的设计。然后,我相信了。
任何人学某个东西,经历了一个模糊,甚至头破血流的撞墙后,都会留下自己的一点痕迹。这痕迹来自你自己对这家伙的一点认识,或深或浅的,纵使有一天,会产生新的,更有深度的,更本质的理解,至少,它是你现在的理解,能够帮你解释,甚至解决当前面临的问题。
主角呢?问的好奇怪,不就在题目中吗?只是想自己对设计的一点理解记下而已。这或许是我本人对设计模式,当前的,比较深刻的理解。
OK,什么是设计模式?人们在解决一个问题时,都总有这样那样的解决方案,其中不乏一种比较好的方案,那么我们对该种方案的抽象,变成了一种设计模式,可以说,设计模式是一种抽象的,而非具体的思想性的东西。GOF里有23种设计模式,其实没人说,设计模式就只有这几种,本文主要对其中部分模式进行研究。
在开始之前,我会说明两点,第一,为了简单起见,我暂不遵循面向对象设计思想里的某些原则,比如说面向接口编程,面向抽象编程,OCP原则等等。但是,你会疑问,这些原则都没有,你的设计在哪里,也许那是种很糟糕的设计,但是需要注意到的是,真正项目中,没有一个优秀的设计者,会不大致遵循这些原则,我说了--简单起见,把你带到核心的,本质性的东西,而非带你去凌乱的丛林中...(很可能,你会象兔子一样,见到萝卜,把白菜丢了,我知道,你也不想那样,可惜没办法,程序员的记性就是这样子),有一天,你会发现设计模式就这么简单,应用的话,不包含于此。
内容简介:1.委托的使用..1.1 简单委托(反向委托) 1.2 双向委托 1.3 多重委托 1.4 双向多重委托
2.回调
3.继承与委托
4 多态
5.主要模式:iterator, adater, chain of responsibility, builder, proxy, decorator, (template)strategy,bridge, state, visitor,observer, command, mediator.
这些模式的编排依照文中出现的顺序,并且遵从简单到复杂。其他模式,我会暂时放过他们。
1 委托
委托,顾名思义,很简单,有些事,或者你不会或者你懒,你交给了另一个人帮你做,这就是委托。但是,请记住一点,这里的委托有他拓展的意思,我把所有交给别人做事这一过程定义为委托,可能你会在某些书上看到它--你小子放屁,会被你误倒的,我会很虚心承认这一点的。但是,它对我有效,如果发现它起到了side effect(副作用) on you,或许你得停止这场糟糕的旅行。回到你原来的地方。
看了很多文字,很是糟糕,很多人不喜欢,包括我也是,我是说之前的我,但是现在我会很喜欢一些理论性的东西,然后里面加点实践的味道进去。真是佩服牛人,已经到了那种境界。既能当牛,又同时是人。
1.1 简单委托:我们定义两个类A,B,然后让B来委托A做事(不给钱的,
),但是会有代价。
Class A{
methodA() {}
methodA'() {}
}
Class B{
A a;
methodB() {
a.methodA();
}
methodB'() {
a.methodA'();
}
}
void main(){ //这不是我们的目标,是一个可协商的客户,难得碰上这么好的客户
B b = new B();
b.methodB();
}
代码很简单,客户期待的是b对象,让他处理,然后,对象b包含的委托对客户是透明的,也就是说main压根不知道有A这样的一个类存在。但是,目的没达到,很糟糕,出现了NULL Pointer,最糟糕的也是最好解决的错误。接下来,得实例话a对象。
方案1:分别改类B和main为
Class B{
A a;
B(A a) { //多了构造函数,给a附一实例
this.a = a;
}
methodB() {
a.methodA();
}
methodB'() {
a.methodA'();
}
}
void main(){ //多么和蔼的客户,他得根据我们来调整,感动....
(1) A a = new A(); //当然,将(1)(2)写成B b = new B(new A());会显得简洁
(2) B b = new B(a);
b.methodB();
}
待续......
继续...... 2008-04-15
方案(2): 我们可以在类B中设置一个方法,setA来给B传递A实例,去掉刚才的构造函数。
Class B{
A a;
void setA(A a){
this.a = a;
}
methodB() {
a.methodA();
}
methodB'() {
a.methodA'();
}
}
void main(){
B b = new B();
b.setA(new A());
b.methodB();
}
OK,有效果了。然后,我们怀疑这样的逻辑,客户跟某公司的经理谈判项目的协议签定问题时,即使该经理把这事交给了下属去完成,客户也得去了解那下属家伙究竟做了什么,客户知道的太多了。很不好(就象程序员完成某功能时,他得知道很多细节,这样的编程方式比较危险,为什么会这样呢?我们得认真考虑这问题,而这也是现在的想法,我们会想着尽量美好的憧憬),我们继续走下去,下面会有更好点的方案。
方案(3): 我们把对象a的实例化安置到了类B里面,那么这样的话,依赖的一方必须了解被依赖者,而客户压根不懂委托者。
Class B{
A a;
B(A a){
this.a = new A();
}
methodB() {
a.methodA();
}
methodB'() {
a.methodA'();
}
}
void main(){
B b = new B();
b.methodB();
}
如果,用setA函数替代构造函数,会得到方案(4)
方案(4):
Class B{
A a;
void setA(){
this.a = new A();
}
methodB() {
a.methodA();
}
methodB'() {
a.methodA'();
}
}
void main(){
B b = new B();
b.setA();
b.methodB();
}
到此,简单的委托处理就先告一段落。发现没有,这四种(其实只有两种)方案我们在新框架出来之后,我们改了名字---注入对象实例。然后,后来(当然是很早以前),配置文件方式的注入,如spring框架,取了个叫什么依赖注入,反转控制来着。也就是这东西确实很简单,没啥好说的,但是,我又说了,是因为,这跟设计模式有莫大的关系,至少我这么觉得。 然而,我还是得说,以上两种方案各有好处,在一些简洁的设计中,我们会习惯于构造类B时同时给被委托者创建实例,一旦,发现被委托者是动态被注入或者被委托者被多次注入时(可以是委托集合),我们会偏向于(2)(4)注入方式。你会发现设计模式大多是这两种方式的。
方案(5): 反向委托:客户面向B,而B万一很忙或不在呢,或者干脆你没权利联系B,也就是你没法实例化一个B,然后,我们可以通过另一种方式来获取B实例。我称它为反向委托--可能不恰当,我没经过太多考虑.
代码修改如下:在类A中添加getB方法,通过实例A来获取B实例。
Class A{
B getB() {
return new B(this);
}
methodA() {}
methodA'() {}
}
Class B{
A a;
B(A a){
this.a = a;
}
methodB() {
a.methodA();
}
methodB'() {
a.methodA'();
}
}
void main(){
A a = new A();
B b = a.getB();
b.methodB();
}
如果看过设计模式,细心的你也许会发现了一个这东西很类似于Iterator模式,a是个集合对象,b是个迭代器,可以通过一个迭代器访问集合类,把数据的操作和数据分开了。当然此处a,b都是普通类,a不是集合,b也不是迭代器。具体的演化过程,接下来的章节将继续讨论。
1.2 双向委托
看过简单委托,估计聪明的你猜到了,双向委托就是互相包含依赖对方。没错,确是这样的。看例子:
Class A{
B b;
A(B b) {
this.b = b;
}
methodA() {}
methodA'() {}
}
Class B{
A a;
B(){
this.a = new A(this);
}
methodB() {
a.methodA();
}
methodB'() {
a.methodA'();
}
}
void main(){
B b = new B();
b.methodB();
}
这是种高度耦合的结构,在他们的世界里,谁都离不开谁,类似于,邻居关系,咱家没的东西去找邻居借,邻居没的东西会来找咱家借一样。当然,这似乎不是很好的设计,但是确实经常碰到,也很有用。可以看看敏捷软件开发里的线程控制的模拟实现---active object模式,虽然,它不是经典模式之一。
继续我们的旅程--2008-04-16
1.3 多重委托
一个类中包含多个被委托者,而这委托是单向的,当然这些被委托者可以是同类型--称之为委托集合,我们把这种结构定义为多重委托。例子:
Class A{ methodA() {}
methodA'() {}
}
Class A1{ methodA1() {}
methodA1'() {}
}
Class A2{ methodA2() {}
methodA2'() {}
}
Class B{
A a;
A1 a1;
A2 a2;
B(A a, A1 a1, A2 a2){ //此处我们也可以分别定义三个类似setA这样的方法实例化他们
this.a = a;
this.a1 = a1;
this.a2 = a2;
}
methodB() {
a.methodA();
}
methodB1() {
a1.methodA1();
}
}
void main(){
A a = new A();
A1 a1 = new A1();
A2 a2 = new A2();
B b = new B(a, a1, a2);
b.methodB();
}
就是这种结构,类B委托了3个其他类A,A1,A2。这里我们也可以分别定义三个类似setA这样的方法实例化他们,同时我们也喜欢这样做,因为世界是变化的,你也得遵循这样的约定。这只个表示多重委托的例子,它是个原型,他还未能用于我们的设计中,你会发现这种结构有它的缺点,最简单之一,他违反了OCP原则,这个类B将无法扩展,扩展的意义在于不应该通过修改原始代码来完成而应该是通过继承或委托来完成,想象一下,如果你有一段代码,每次增加新功能或修改bug时,你都得把类B修改一遍,这样的结果,很简单,有一天你会被自己写的东西淹没了,你会感觉到一种恐慌,很无助,无奈于客户的抱怨声中,同时,也很难保证项目的维护成本。然而这样的结构确实对我们的设计具有启发性的意义。我们积极去改写这样的结构,使它慢慢处于我们的掌控中,然后,你会有惊奇的发现。
想要扩展性,我们可以让这些被委托者以集合形式保持住。于是,我们想到观察者模式,没错,就是这样的一个例子。你还想到什么模式呢?
1.4 双向多重委托
双向多重委托是比较复杂的结构,它其实应该是个网状模型,每一个委托者同时又是被委托者,学过数学的你,自己算算其复杂度。然而,问题在于解决问题时应该尽量简单化,没有任何一个司机喜欢,在乘客多时,让乘客划拳决胜负后再上车。OK,这样的结构我们会尽量少用,转为利用上面多重委托的变形,在类B中保持多个被委托者的同时,让各个被委托者持有一个委托者,即类B。
2.回调
回调这个词,你肯定早听说过了,没啥好说的,不清楚的百度一下,这里说这个,也啥其他意思。这只是我思考该主题的一个byproduct(副产品),我把对象与对象之间的穿梭行走定义为了回调。
Class A{ methodA(B b) {
b.methodB'();
}
methodA'() {}
}
Class B{
A a;
methodB() {
a.methodA(this);
}
methodB'() {
System.out.println("methodB'");
}
}
void main(){
B b = new B();
b.methodB();
}
这样就能实现类B的回调,也许你会认为为什么B不直接调用它自己呢?反而绕了一圈呢,相信聪明的你,以后会慢慢理解的,在这里,回调不是主角,同时,如果你想让这个回调永远持续下去,那你就去做吧。
3.继承与委托
讲了一堆的委托概念--其实不是这些都不经典理论,只是个人的一点想法而已,该看看继承了,它已经呆不住了。其实,学过面向对象编程的人都知道,这里只是理理其关系罢了。有一个纂书人,说了一句经典之言,继承是生离死别,委托是潇洒分离,我很喜欢这样的描述,这句话提到了,对象与对象之间的耦合性在这两者之间的体现,每个人都知道其耦合性应该降到最低层次。然而继承给我们带来的麻烦,他纠缠于父类与子类之间,例子还在整理中....所以,牛人又说了,尽量用委托代替继承。然后,我又相信了。但是,得确认的一点是,继承也是面向对象的组成之一,我们该权衡之使用。我也自己经常用它。
4 多态 --暂略过
在以后的部分,即将开始真正的内容,它将引领你一起去...,你想去哪里?
posted on 2008-08-13 16:53
zhqh 阅读(206)
评论(0) 编辑 收藏 所属分类:
设计模式