Design by Use (DBU)
DBU is a set of software design and development techniques which I have found very useful during my career. I recognize that the parts that make up DBU are not new to everyone.
Before I go into a general description of DBU and compare it to OOD, DbC, and TDD I want to point out some unique aspects of DBU.
Unique Aspects of DBU
DBU considers large software development issues and specifically multiple teams working simultaneously to build components and subcomponents which ultimately will work together as a software system.
DBU describes what is termed "immediate integration". For me this was a new concept. For you it may not be, or maybe I have not communicated clearly what I mean.
Suppose there are two teams, Team A and Team B.
Suppose that Team A is writing Component A which depends on Component B which will be developed by Team B.
Team A writes inside of Component A the call to Component B before Component B is developed. Team B takes the code from Component A and uses that to define the method signature or interface into Component B. Team A decides how they want to use Component B. Team A codes the preferred usage and gives that to Team B.
Team A writes this "preferred usage" code very early in the development of Component A. This is done early so that Team B can start as soon as possible so that all teams are working on their components in parallel as much as possible. When I say "very early" I mean at first for most situations.
Notice that Team A specifies the first version of the interfaces for Component B which are of interest to Team A. As with most software, changes to the interfaces occur before finishing the product. I shouldn't even have to say that, but so many read a description and then say, "You don't allow for future changes." All I can say is that people who think like that need to take the blinders off. If I don't describe some particular issue that you think is important I say to you can you imagine a way to address your issue and if so then everything is still good.
So, Team A writes inside of Component A the "preferred usage" code for the call to Component B, and then creates a stub for Component B. Team B takes ownership of this stub and brings it into Component B and Component A no longer calls the stub but calls Component B. Thus we have immediate integration between Component A and Component B. This new call into Component B had the pre-conditions, post-conditions, and invariants that are concerns for Team A specified by Team A. These concerns can be used in the definition of automated tests.
Team B does not have to wait and wait for Team A to finally decide to call their system. Team A does not have to worry about Component B's interface and how to match up the classes, structures, parameters, exceptions, or return values. There will be no useless code and design based on the common tactic of "We will go ahead and design and implement Component B and when you finally figure out how you want to call us we can implement a mapping layer between the systems." What a poor way to do parallel component development.
Notice that Team B did not declare to Team A that Component B will have these interfaces and Team A will have to figure out how to create the data necessary to make the call. In the development of "NEW" software the "user" has priority over the "used". Some may say, "This doesn't work for integrating software to existing systems." That's right, it doesn't have anything to do with integrating to existing systems such as third party libraries, unless you are designing a transformation layer between your system and the third party system. If you are designing a transformation layer then I would do it in the DBU fashion.
Component B should only do what its users need it to do and nothing more, and obviously nothing less. Any extra code is just a waste. Mapping layers sometimes are the sign of poor design or poor utilization of teams and are just unnecessary and extra code.
Team A knows critical constraints that Team B will not know. For instance, there may be a performance constraint. Suppose Component A must return results in 1 second. That means Component B must return its results in less than 1 second. Team A knows this requirement and passes it down to Team B by means of the "preferred usage" which is stubbed out and called by Team A with the appropriate error code if the call into Component B takes too long. When Team B takes ownership the stubbed code and moves it into Component B then Team B will have reference to the timing constraints and proceed accordingly.
DBU in its Simplest Form
In its simplest form DBU is similar to Test First Programming. The developer, on an individual basis, must start writing code somewhere and in some direction. After appropriate domain consideration the developer will start building classes, structures, or even data flows. It doesn't matter if you are Object Oriented, Structured, or Procedural, there is an architecture that corresponds to your development method.
The direction choice is made by writing calls as if they already exist. Thus you are designing the method on how you are going to use it. The parameters to the call will be of the type that you have available. The results of the call will be of a type that you want to handle. This is by its very nature low level code design. DBU does not require you to have a high level design nor does it exclude the use of a high level design. DBU does not need a detailed low level design before coding because DBU creates the low level designed as needed, in context, on time, in place, and correct for the situation.
That is how you get started designing and writing new code. It is a very powerful way to do so.
DBU is applicable to modifying existing code. Often I find myself adding to existing code. I struggle to organize new code with existing code. I find myself trying to use what already exists instead of trying to use the code the way I would prefer. As I group calls to existing code I often feel that I am ruining the architecture or that this really doesn't fit. I often get stuck and can't figure out how I am going to get the data from all of the places I need and transform it to how it is needed. Then I remember, "Hey dummy, write the new code how you would prefer it to be, even if it doesn't exist." When I do this the code flows, the architecture is maintained or extended but it is not violated or hacked. Every time I have done this I have been pleased with the results. Yes, every time.
I have previously blogged concerning DBU and database design and how it has helped me with SQL queries and such.
DBU and Object Oriented Design
DBU is applied at low level / code level design. Therefore DBU works well with Object Oriented Design (OOD). Sometimes I design my domain objects using UML. I feel it is very important to gain as much understanding of the domain as possible before the low level code design begins. I define the objects and then I usually go right to sequence diagramming in order to imagine or simulate interactions. I do not "flesh out" the method calls to any great extent in UML. But that is me. You do what works for you. I do not use UML to generate my code. I use it to define meta data, organize thoughts on the domain, and get me pointed in the right direction. On small tasks where the domain is simple or in areas where I have lots of domain knowledge I do not even do UML.
DBU and Design By Contract
DBU uses aspects of Design By Contract (DbC). There are three questions associated with DbC.
1) What does it expect?
2) What does it guarantee?
3) What does it maintain?
DbC is based on the metaphor of a "client" and a "supplier". In DBU the user is the "client". In DBU the user of the "to be developed" method defines the preconditions, postconditions, and invariants on externally visible state. DBU follows the same rules of DbC for extending the contract down into lower level methods and procedures, such as a subclass may weaken a precondition, a subclass my strengthen a postcondition, and a subclass may strengthen invariants.
Design by Use and Test Driven Development
DBU and Test Driven Development (TDD) have similarities but are different. Both are design activities. In my opinion both are low level code design activities.
Some definitions of TDD require you to write a failing test (which is similar to a usage example of DBU) and then run your testing framework and see the indication that the test fails. You may do that in DBU but it is not a requirement of DBU. I want to point out that many will say you are not doing TDD unless you write a failing test and then watch it fail. DBU is not thusly constrained.
DBU is defined for new development and for modifying existing code. In DBU if you are modifying existing code and you are developing new functionality you do it in place, in context, in state, where it is needed. You write the new code as if it already exists. Of course the new code isn't going to compile and you don't have to compile it to see it fail. Now if you want, and this is something I personally do, you take this new code and you put it into a "programmer's test" so that it will benefit from automated regression testing. You can put the new code into the tests before you actually develop the underlying functionality if you want and drive the development underlying functionality from the test, or in other words at this point you can use TDD. Or, you can continue in the existing code and use your IDE to generate the method and then fill in the functionality whilst considering DbC and then place calls to the new code in the "programmer's tests".
DBU is concerned with designing the call to the new code in context with the data that is on hand or accessible. DBU does not get stuck on such things has what needs to be public or private or if everything has to be public so that I can fully unit test the code. DBU designs code as needed and needed code is code that is called and code that is called is exercised and code that is exercised is tested.
Am I saying that all possible states are exercised. No. I don't think that TDD promises that either. Why, because in TDD the unit tests are still written by humans who have a finite amount of knowledge, time, and attention.
If the method you have just defined is visible to other classes or callers then I refer you back to DbC to state what is expected.
Summary
Design By Use defines "Immediate Integration" where the user specifies the inputs, outputs, and method name (or in other words the method signature). Once the user of the new method has defined the preferred method signature and constraints the team that will develop the new method works from the users definition to build the actual functionality. These component boundaries or interfaces are defined early so that all teams may work in parallel and so that the components are linked together immediately at definition time and not at some far off date.
DBU avoids the unnecessary code of mapping layers that result from poor communication, downstream waiting, or teams going off in their own direction.
DBU is a low level code design activity. It works well with OOD, DbC, and TDD.
DBU applies the user's preference on how things should be called to existing code as well as new code. When modifying existing code DBU says to write the modifications in the way that seems best even if the code doesn't exist. By doing this the overall structure and architecture of the system is extended and not just hacked and coupled. I do not know of any other low level code design methodology that follows that principle. There could be many. I just don't know them or maybe they don't have a cool name like Design by Use!
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment