小故事:架构师需要做什么?

jopen 6年前

英文原文:A Little Architecture  

作者:Robert C. Martin  
译者:孙薇

本文是一篇模仿问答的小故事,作者用幽默的风格简单分析了架构师要做的工作: 我想要成为一名软件架构师。 

这是年轻软件开发者很好的选择。

我想要带领团队,并在数据库与框架、webserver 等方面作出重要的决策。 

噢,那你根本就不想成为软件架构师。

我当然想了,我想要成为重要决策的制定者。 

那很好,不过你列出的内容中并不包含重要的决策,这些都是不相关的决策。

什么意思?你是说数据库并不是重要的决策,你知道我们在上面花了多少钱吗? 

也许花的太多了。但是,数据库并不是重要的决策之一。

你怎么能这样讲?数据库是系统的核心,是进行所有数据系统化、分类、编入索引和存取工作的地方;没有数据库的话,就不会有系统。 

数据库只是一个 IO 设备,它恰巧为分类、查询与信息报告提供了一些有用的工具,但这些都只是系统架构的辅助功能而已。

辅助?这太离谱了。 

没错,就是辅助。系统的业务规则也许能够利用其中的一些工具,不过那些工具却并非相应业务规则所固有的。需要的话,可以用不同的工具来替换现有的这些;而业务规则不会改变。

嗯,没错,不过必须重新进行编码,因为在原本的数据库中这些工具都用到了。 

那是你的问题。

什么意思? 

你的问题在于,你以为业务规则是依赖数据库工具的,实际上并不是。或者说至少,在提供优秀架构前并不应当是这样的。

这简直是疯了。如何创建不使用那些工具的业务规则呢? 

我不是说它们没使用数据库的工具,而是说它们并不依赖于此。业务规则无需知道你使用哪个数据库。

那么如何在不了解使用什么工具的情况下,获得业务规则呢? 

让依赖倒置过来,使得数据库依赖业务规则。确保业务规则不依赖于数据库。

你在胡言乱语。 

恰恰相反,我在使用软件架构的语言。这是依赖倒置原则:低层准则应当依赖高层准则。

一派胡言!高层准则(假设指的是业务规则)调用低层准则(假设指的是数据库)。因此高层准则会根据调用方依赖被调用方的原则,而依赖低层准则。这个谁都知道! 

在运行时的确如此。不过在编译时,我们想要的是依赖倒置。高层准则的源代码应当不提及低层准则的源代码。

得了吧!怎么能在不提及的情况下进行调用呢? 

当然没问题。这就是面向对象的所涉及的内容。

面向对象是关于真实世界的模型创建,将数据、功能与有凝聚力的对象相结合。是关于将代码组织成直观的结构。 

他们是这么说的?

大家都知道,这是显而易见的真相。 

没错,确实如此,然而,在使用面向对象准则时,的确可以在不提及的情况下进行调用。

好吧,那要怎么做? 

在面向对象设计中,各个对象会彼此发送消息。

没错,这是当然的。 

而 sender 在发送消息时,并不知道 receiver 的类型。

这取决于所使用的语言。在 Java 中,sender 至少知道 receiver 的基础类型。在 Ruby 中,sender 至少知道 receiver 能够处理所收到的消息。 

没错。不过在任何情况下,sender 都不知道 receiver 的具体类型。

是这样,好吧,确实如此。 

因此,sender 可以在不提及 receiver 具体类型的情况下,设计 receiver 执行某个功能。

是这样,没错。我了解了。不过 sender 仍旧依赖于 receiver。 

在运行时的确如此。不过编译时则不同。sender 的源代码并不会提及或者依赖 receiver 的源代码。事实上 receiver 的源代码依赖于 sender 的源代码。

不会吧!sender 仍依赖于它所发送的类。 

也许从某些源代码来看,会更清楚一些。下面这段是 Java 写的。首先是 sender:

package sender;public class Sender {        private Receiver receiver;    public Sender (Receiver r) {      receiver = r;    }    public void doSomething () {      receiver.receiveThis ();    }    public interface Receiver {      void receiveThis ();    }  }

下面是 receiver: 

package receiver;import sender.Sender;public class SpecificReceiver implements Sender.Receiver {    public void receiveThis () {      //do something interesting.   }  }

注意:receiver 依赖于 sender,SpecificReceiver 依赖于 Sender,在 sender 中并没有 receiver 相关的信息。

是啊,不过你在撒谎,你把 receiver 的接口放在 sender 类中了。 

你开始懂了。

懂什么? 

当然是架构的原则。Sender 拥有 receiver 必须实现的接口。

如果这意味着我必须使用嵌套类,那么…… 

嵌套类只是实现目的的手段之一,还有其他办法。

好吧,等一下。这又跟数据库有什么关系?我们最开始讨论的可是数据库。 

再看一点代码吧。首先是一个简单的业务规则:

package businessRules;import entities.Something;public class BusinessRule {    private BusinessRuleGateway gateway;    public BusinessRule (BusinessRuleGateway gateway) {      this.gateway = gateway;    }    public void execute (String id) {      gateway.startTransaction ();      Something thing = gateway.getSomething (id);      thing.makeChanges ();      gateway.saveSomething (thing);      gateway.endTransaction ();    }  }

业务规则没占多大份量。

这只是个例子。还能有更多这样的类,实现很多不同的业务规则。

好的,那么 Gateway 到底是什么?

它通过业务规则提供了所有数据存取方法。按以下方式实现:

package businessRules;import entities.Something;public interface BusinessRuleGateway {    Something getSomething (String id);    void startTransaction ();    void saveSomething (Something thing);    void endTransaction ();  }

注意:这是在 businessRules 之中。 

ok,Something 类又是什么? 

它代表着简单的业务对象。我将它放在 entities 之中。

package entities;public class Something {    public void makeChanges () {      //...   }  }

最终 BusinessRuleGateway 实现,这个类知道真正的数据库:

package database;import businessRules.BusinessRuleGateway;import entities.Something;public class MySqlBusinessRuleGateway implements BusinessRuleGateway {    public Something getSomething (String id) {      // use MySql to get a thing.   }    public void startTransaction () {      // start MySql transaction   }    public void saveSomething (Something thing) {      // save thing in MySql   }    public void endTransaction () {      // end MySql transaction   }  }

另外,注意业务规则在运行时调用数据库;不过在编译时,数据库会涉及并依赖于 businessRules。 

好吧,我想我明白了。你只是在利用多态性来隐藏从业务规则实现数据库的事实。不过仍需要一个接口,向业务规则提供所有的数据库工具。 

不,完全不是这样。我们没有尝试向业务规则提供数据库工具。而是通过业务规则,为它们所需要的内容创建接口。实现这些接口就能调用合适的工具。 

是啊,不过如果所有业务规则需要用到每个工具,那么只需把工具放在 gateway 接口中。 

啊,我看你还是没明白。 

明白什么?这已经很清楚了。 

每个业务规则只为自己所需的数据访问工具定义一个接口。

等一下,你说什么? 

这就是接口隔离原则(Interface Segregation Principle)。每个业务规则类只用到数据库的某些设施。因此,每个业务规则提供的接口只能访问相应的设施。

不过,这意味着需要很多接口,以及很多的小型实现类,它们又会调用其他的数据库类。 

很好,你开始理解了。

不过这太乱了,浪费时间。为什么要这样做呢? 

这样做能够条理分明,节省时间。

得了吧,为了代码,弄出来一大堆代码。 

恰恰相反,通过重要的架构决策,可以延缓不相关的决策。

这是什么意思? 

记得最开始,你说想做软件架构师不是吗?你想要作出所有真正重要的决策。

是啊,我是这样想的。 

你想要决策的是数据库、webserver 和框架相关的方面,对吗?

是啊,你说那些都不重要。只是不相关的内容。 

没错。就是这样。软件架构师所作出的重要决策指的是,让你不对数据库、webserver 和框架进行决策。

不过必须得先决定那些吧! 

不用的。事实上,在开发周期中,这些都可以稍后再决定,在信息更充足的时候再决定。 

如果架构师提前确定框架,却发现框架无法提供所需的性能,或者带来了无法忍受的约束,这就成了灾难。

只有架构师决定推迟决策,待信息足够时才作出决策;在架构师的决策下,不使用缓慢而过于耗费资源的 IO 设备和框架的团队,才能创建快速、轻量级的测试环境;只有其架构师关心真正重要的东西,延缓那些不重要的,这样的团队才是幸运的团队。

胡说,我完全不明白你的意思。 

好吧,还是好好看一下本文,不然只能再等 10 年你才能明白了。

来自: www.iteye.com