Driven to … Discovering
Your Design Values
驅動……發現你的設計價值
Rebecca J. Wirfs-Brock
There will be variations of everything forever. … Ideas don’t disappear. They change form,
they merge with other ideas. —Bob Frankston, coinventor of VisiCalc
萬物永遠都是變化的……思想沒有消失。它們改變了形式,它們并入了其他思想。——Bob Frankston,VisiCalc發明者
Today we have design or development approaches that are, for example, responsibility driven (RDD), test driven (TDD),behavior driven (BDD), domain driven(DDD), and model driven (MDD). Not all thought leaders in software development have been “driven”—Bertrand Meyer, for example, invented Design by Contract. But whether “driven” or not, these approaches all emphasize a core set of values and principles around which practices, techniques, and tools have emerged.
今天我們擁有的設計或開發方法是,舉個例子,責任驅動(RDD),測試驅動(TDD),行為驅動(BDD),領域驅動(DDD)和模型驅動(MDD)。并不是軟件開發中的所有領導者都是“驅動”的——比如Bertrand Meyer發明的契約式設計。但是無論是不是“驅動”,這些方法都強調了一組圍繞已經出現的實踐、技術和工具的核心價值和準則。
A thoughtful designer should be able to pick and choose among practices without losing their essence. But not all practices are congruent. After stewing in this alphabet soup for years, I’m keen on exposing the common and complementary threads that are interwoven among various design practices.
一個有思想的設計者應該能夠在這些實踐中挑選而不失去它們的本質。但是不是所有的實踐都是適合的。在這個字母湯里煮了多年之后,我熱衷于揭示交織在各種各樣的設計實踐中的共通之處和補充內部。
Responsibility-driven design
責任驅動設計
So how can you integrate various practices without watering them down or muddling your thinking with too many considerations? It’s certainly easy if you have one belief system with one small set of coherent values and practices that guide your work. In 1989, Brian Wilkerson and I authored the paper “Object-Oriented Design:A Responsibility-Driven Approach.”1 For better or worse, we started the trend of tagging design approaches as “driven.” To make our point, we oversimplistically divided object design approaches into two camps: those that focus first on structure and those that focus first on action (or responsibilities or behaviors). We argued that designers who first focus only on an object’s structure fail to maximize encapsulation.
Thinking too early about structure makes it too easy for implementation details to bleed into class interfaces.
所以你怎樣將各種不同的實踐整合起來而不會由于有太多需要考慮的事項將它們全都隨水一起倒掉或搞亂你的思想?如果你有一個信任系統和一小套一致的價值和實踐指導你工作,那么這確實很容易。在1989年,我和Brian Wilkerson寫了名為“面向對象設計:一種責任驅動的方法”的論文。為了更好或更糟,我們開始傾向于給設計方法貼上“驅動”的標簽。為了聲明我們的觀點,我們過分單純的將對象設計方法分為兩個陣營:一派首先關注結構,而另一派首先關注動作(或責任或行為)。我們認為首先只是關注一個對象的結構的設計者未能實現最佳的封裝。過早的考慮結構會導致過早的將實現細節侵染到類接口。
We contrasted two approaches for designing a RasterImage class that represented a rectangular grid of pixels. (We wrote this paper when raster technology was new, and at the time we both worked at Tektronix, a leading provider of graphics workstations.) With a data-first approach,we started defining our RasterImage class by declaring the image data structure and then adding methods to retrieve and set the image and query its dimensions. Voila!—a class where form and function were inextricably intertwined.
我們對比兩種設計表現像素的一個矩形柵格的RasterImage類的方法。(我們寫這篇論文時,光柵技術還是新的,那時我們一起在Tektronix工作,Tektronix是圖形工作站的主要提供商。)用數據先行的方法,我們開始定義我們的RasterImage類,我們聲明鏡像數據結構,然后添加方法去重新得到和設置鏡像,查詢它的尺寸。瞧!——一個外形和功能避免不了的糾纏在一起的類。
The pixel grid data structure wasn’t considered a private implementation detail.Next, we demonstrated how a designer could think differently about the problem by asking,“What actions could this object be responsible for?” and “What information should it share with others?” This led us to first define operations for our RasterImage class to scale and rotate the image and access pixel values. The internal image representation, which we didn’t specify until after we’d defined the interface,was considered a private detail.
這個像素柵格數據結構不是被考慮成一個私有的實現細節。下一步,我們證明一個設計者能夠以不同的方式來思考問題,通過詢問“這個對象對于什么動作負責任?”和“它和別人共享什么信息”。這引導我們首先定義我們的RasterImage類的操作來測量和旋轉鏡像和訪問像素值。我們在定義接口之前沒有指定的內部鏡像表示被考慮成一個私有細節。
By consciously assigning most objects actionoriented responsibilities, you can design even seemingly data-centric objects to perform some actions as well as encapsulate structural details.
Hiding that structure makes those details easier to change. To us it seemed that the order in which a designer considers things profoundly affects the resulting design—even for a class as straightforward as RasterImage. To quote Samuel Alexander, the philosopher, “An object is not first imagined or thought about and then expected … but in being actively expected it is imagined as future and in being willed it is thought.”
通過自覺地分配給大多數對象面向動作的責任,你甚至能夠設計看起來以數據為中心的對象來執行一些動作和封裝結構的細節。隱藏結構使那些細節容易改變。對于我們而言,看起來一個設計者考慮事情的順序深深的影響作為結果的設計——甚至對于一個像RasterImage一樣簡單易懂的類。引用哲學家Samuel Alexander的話,“一個對象不是先被想像或思考然后被期望……但是由于積極的被期望,它被想像成未來,由于有某種意志,它是思想。”
Since those early days I’ve added the notion of role stereotypes, 2 acknowledging that not all objects are active. Information holders—objects with responsibility for maintaining data—have a place in a design, too. But encapsulating their private details is important.
自從那些早些的日子我添加了角色構造型的概念,知道不是所有的對象都是有效的。信息持有者——具有持有數據責任的對象——也在一個設計中有一席之地。但是封裝它們的私有細節是重要的。
Test-driven design
測試驅動設計
RDD evolved in the highly interactive world of Smalltalk development, where developers routinely designed a little, coded a little, and tested a little in short cycles. The delightful tension of cycling between imagining what an object might do, building it, and then refining your ideas and cycling through your design again can lead to deep insights. This has led agile-programming thought leaders to promote test-driven development practices. Test-driven design emphasizes deciding on an interface and then writing code to test that interface, before implementing code to make the interface pass the test.
RDD在高度互動的Smalltalk開發世界進化,在那兒開發者例行公事的設計一小塊,編一小段代碼,然后再很短的周期里做少許測試。在想象一個對象做什么,然后構建它,然后精化你的想法之間循環的令人愉快的壓力而且循環通過你再一次設計能夠使你有更深的理解。這引導敏捷開發思想的領導者提倡測試驅動開發實踐。測試驅動設計強調動對一個接口作出決定,然后編寫代碼去測試那個接口,在實現代碼之前使接口通過測試。
In Test-Driven Development by Example, Martin Fowler claims that TDD “gives you this sense of keeping just one ball in the air at once, so you can concentrate on that ball properly and do a really good job with it.”3 With a designtest-code-reflect-refactor rhythm, good code emerges alongside well-designed interfaces.
在Test-Driven Development by Example中,Martin Fowler聲稱TDD“給你這種保持一次只有一個球在空中的感覺,所以你可以完全集中精力在那個球上和使用它真正地做好一件事。”帶著一種“設計測試-編碼-反饋-重構”的節奏,好的代碼在一旁顯示出設計良好的接口。
Linda Crispin explains that TDD isn’t really about testing.4 Instead, it’s a practice that gets you thinking about as many aspects of a feature as you can before you code it. With frameworks such as Fit and Fitnesse, TDD has extended beyond its initial focus on just developers to enable nontechnical people to write concrete examples of inputs and expected results in tabular form. Programmers write test fixtures that use these behavioral specifications to test the code.
Lina Crispin說明TDD其實不是關于測試的。相反,它是一種使你在編碼之前盡可能考慮一個特征的許多方面的實踐。使用像Fit和Fitness這些框架,TDD擴大了它最初的關注范圍,那時它只關注開發者使非技術人員能夠在表格中寫出具體的輸入樣板和期望的結果。程序員編寫測試裝置來測試代碼,測試裝置使用這些行為的詳細規格說明書。
As TDD practices have grown, new variants of them, along with newer testing frameworks, have emerged. Users of jMock use mocks that mimic unimplemented behaviors to drive out an appropriate distribution of responsibilities among collaborators .5 They don’t think that it’s just about testing, either. Mocking lets you incrementally design and build software, hypothesizing and refining your ideas as you go.
作為一個TDD實踐者已經成長,它們中的新的變體和更新的測試框架已經形成。jMock的使用者使用mocks來模仿未實現的行為來完成在協作者中一個適當責任的分配。他們不認為只是測試。Mock讓你增量的設計和構建軟件,當你前進時假定和精煉你的想法。
Behavior-driven design
行為驅動設計
BDD is another subtle refinement of TDD. BDD proponents firmly believe that how you talk about what you’re doing influences how you work. The focus is on writing small behavior specifications to drive out the appropriate design. As Dave Astels puts it, “A major difference is vocabulary. Instead of subclassing TestCase [as you would do using an xUnit framework], you subclass Context. Instead of writing methods that start with test, you start them with should.”6 Testing, to BDD proponents, connotes verifying code after it’s built. Instead, they want to encourage incremental design by writing small specifications, then implementing code that works according to spec.
BDD是TDD另一種的細微的改進。BDD支持者堅定的相信你如何討論你正在做的事情會影響你如何工作。焦點是編寫小的行為規范來導出合適的設計。當Dave Astels提出它時,“一個主要的不同是詞匯。代替子集化TestCase [當你使用一個xUnit框架時],你子集化上下文。代替書寫使用測試開始的方法,你使用意愿開始。”測試,對于BDD的支持者,意味著在構建之后檢驗代碼。相反,他們想要鼓勵增量設計通過書寫小的規格說明書,然后實現按照規格說明書工作的代碼。
Does every method
warrant a contract?
Probably not. Methods
that don’t cause side
effects probably don’t
need contracts.
Design by Contract
契約式設計
In contrast, Design by Contract (DbC) has roots in formal specifications. To specify how they expect system elements to interact, designers write contracts specifying what must be true before a module can begin (preconditions), what must be preserved during its execution (invariants), and what it guarantees to be true after it completes (postconditions). You could specify contracts for components, services, or even individual methods. However, in practice, most contracts are written at the method level because existing programming languages and tools support work at that level. Writing contracts is easier if the languages and tools you use support them and you have good examples to emulate. The Eiffel language integrates contract support into the language and runtime environment; most other object-oriented languages don’t.
與之對照的,契約式設計根植于正式的規格說明。為了指出他們期望系統要素如何相互作用,設計者撰寫契約詳細說明在一個模塊開始之前什么是必須正確的(前置條件),在執行時什么是必須保持不變的(不變式),在它完成之后它保證什么是正確的。你應該詳細說明組件、服務或者甚至是單獨的方法的契約。然而,在實踐中,更多的契約是在方法級上書寫的,因為既有的編程語言和工具支持工作在那個層面上。如果你使用的語言和工具支持他們而且你有好的例子仿效那么撰寫契約是更容易的。Eiffel語言將契約支持整合進語言和運行時環境;大多數其他的面相對象語言不具備這種支持。
Before looking at Contract4J, a DbC tool for Java, I thought that specifying contracts for languages without built-in support would be clunky. However, using aspect technology, Contract4J automatically weaves aspect-specific contract tests, which are specified in method comments, into your running code. Contracts specified this way leave method code uncluttered with assertion statements and leave a nice documentation trail of how methods should be invoked.
在看到Contract4J(Java的DbC工具)之前,我認為為一種語言指定契約而沒有內置的支持將會非常郁悶。然而,使用方面技術。Contract4J自動將細節方面的契約測試織入你運行的代碼,它在方法注釋中被指出,被這種方式指出的契約使用斷言聲明使方法代碼保持整潔,保留一份好的文檔跟蹤方法是如何被調用的。
If you choose to, you could apply TDD practices to understand your classes’ behaviors and then add contracts to methods whose behaviors you want verified at runtime. But I suspect these two communities differ considerably in their thinking. Some TDD proponents want to discourage after-the-fact verification, which to them seems antithetical to designing for quality. But adding contracts does tighten up how you use classes, theoretically making it easier to catch errors before they propagate.
如果你做出選擇,你可以應用TDD實踐理解你的類的行為然后將契約加入到你想要在運行時檢驗其行為的方法中。但是我懷疑這兩個社區在他們的思想中相當不同。一些TDD支持者反對事后檢驗,這對他們而言看起來與為了質量設計是對立的。但是增加契約加強了你如何使用類,理論上使它更容易在他們傳播之前捕捉到錯誤。
When you change your design, sometimes contracts will naturally change, too. But once your design ideas settle down, you can finalize or add contractual details. But does every method warrant a contract? Probably not. Methods that don’t cause side ef
fects probably don’t need contracts. But I know I’d certainly find it easier to use class libraries if contract specifications were part of their documentation even if they weren’t validated at runtime.
當你改變你的設計,有時契約也將自然的改變。但是一旦你的設計想法穩定下來,你能夠最終定下來或者添加契約的細節。但是每一個方法都保證一份契約?或許不是這樣的,不會導致副作用的方法或許不需要契約。但是我知道我的確發現如果契約詳細規格書是它們的文檔的一部分就更容易使用類庫,即使他們不在運行時被驗證。
Domain-driven design
What about incorporating DDD ideas into your design practice? According to Eric Evans, DDD isn’t a technology or methodology but “a way of thinking and a set of priorities, aimed at accelerating software projects that have to deal with complicated domains” (www.domainlanguage.com/ddd/ index.html). A central activity in DDD is searching for the language that experts use to talk about the problem and then literally reflecting that language in classes and services in a domain layer. Eric believes that, “If developers don’t realize that changing code changes the model, then their refactoring will weaken the model rather than strengthen it.” Creating a domain model is intricately tied to expressing it in working code. Domaindriven design is an active, ongoing process of expressing this domain language in code.
怎樣把DDD想法融入到設計實踐中?按照Eric Evans的說法,DDD不是一項技術獲方法論而是“一種思考方式和一組優先級,目的在于促進不得不解決復雜領域的軟件項目”。在DDD中一項核心活動是尋找專家用來討論問題的語言,然后逐字的將那種語言反映到位于領域層的類和服務中。Eric相信,“如果開發者不了解改變代碼時改變模型,然后他們的重構將削弱而不是增強模型。”創建一個領域模型雜亂的在工作著的代碼中被依靠表達它。領域驅動設計是一種積極的、正在進行的使用代碼表示領域語言的過程。
Model-driven design
模型驅動設計
In contrast, adherents of MDD (some call it model-driven engineering to avoid the Object Management Group trademarked term) first develop a platform-independent model of their system (usually in UML or a domain-specific language) before translation tools transform the model into platform-specific code. MDD practitioners strive to clearly represent system concepts and behaviors with the goal of producing an abstract model, not working code (the translation tools do that for them). This view of model building followed by transformation probably causes the great divide between MDD practitioners and other design schools—even though they share many common design values. After recently listening to and talking with several well-known MDD proponents who were discussing what constitutes well-designed classes, methods, and components, I found myself nodding in agreement with many of their design guidelines.
與之對照的,MDD的擁護者在使用轉換工具將模型轉換成具體平臺代碼之前,先開發一個他們系統的平臺無關模型(通常使用UML貨一種領域規范語言)。MDD開創者們帶著生產一個抽象模型而非工作的代碼(轉換工具替他們來做生產代碼的事情)的目的,努力清晰的表現系統概念和行為。模型遵循轉換構建的觀點或許導致了MDD開開創者與其他設計學派的巨大分隔——即使他們共享許多通用設計價值。在最近聽到和與許多不同的眾所周知的MDD支持者(他們正在討論什么構成了良好設計的類、方法和組件)討論之后,我發現自己非常贊同他們的設計指導思想中的很多內容。
ow you design should be based on your principles and values. Although a big division exists between those who believe the act of coding is what validates the design and those who don’t, you can learn many things about good design from each. My mantra has always been, “Be open to new ideas and techniques that make me a better designer.” I side with Canadian politician Dan Miller, who proclaims, “You know, we have our differences, everybody does, honest, real differences, but I do believe strongly that we as neighbors are drawn together far more than we’re driven apart.”
如何設計,你應該基于你的原則和價值。盡管有巨大的區別存在于那些確信代碼的行為是檢查設計的標尺的人們和他們的反對者中,但是你從他們中能夠學會許多優秀設計的東西。我的箴言一直是,“善于接受新觀點和技術,使我成為更優秀的設計師”。我和加拿大政治家站在一起,他宣稱,“你知道,我們有不同,每個人都一樣,誠實的,真實的不同,但是我深信作為鄰國我們的共通之處遠遠多于我們的區別。”
References
1. R. Wirfs-Brock and B. Wilkerson, “Object-Oriented Design: A Responsibility-Driven Approach,” Proc. 1989 ACM SIGPLAN Conf.
Object-Oriented Programming, Systems, Lan?
guages, and Applications (OOPSLA 89), ACM Press, 1989, pp. 71–75.
2. R. Wirfs-Brock, “Characterizing Classes,” IEEE Software, Mar./Apr. 2006, pp. 9–11.
3. M. Fowler, Test-Driven Development by Example, Addison-Wesley, 2003.
4. L. Crispin, “Driving Software Quality: How Test-Driven Development Impacts Software Quality,” IEEE Software, Nov./Dec. 2006, pp. 70–71.
5. S. Freeman et al., “Mock Roles Not Objects,” Companion to 19th Ann. ACM SIGPLAN
Conf. Object-Oriented Programming, Sys?
tems, Languages, and Applications (OOPSLA 04), ACM Press, 2004, pp. 236–246; www. jmock. org/oopsla2004.pdf.
6. D. Astels, “A New Look at Test-Driven Development,” http://blog.daveastels.com/files/BDD _Intro.pdf.
Rebecca J. Wirfs-Brock is president of Wirfs-Brock Associates and an adjunct professor at Oregon Health & Science University. Contact her at rebecca@wirfs-brock.com.