"is to alleviate the stress of change."
"Everything should be made as simple as possible, but not simpler" - Albert Einstein
Overview
Since software will change, we should design it to minimize the impact of those changes to the best of our ability. Making code changes is risky, even with testing, anytime you change something that works, it is a risk. Designing your code which minimizes your need to delve into all areas of code should you need to make a change, is vital. Separating code well enough, to allow places that change often to be in isolation from those parts that don't is a good AgileDesign practice.
This is not Agile Methodologies, but rather agility within the design and development of your software. The gap between the Agile Methodology and developers' code is captured here in AgileDesign. This principle is concerned with being "dynamic", "extensible", "pluggable" and quick to change, decreasing or even eliminating the "risk/stress of change". The other weights in Ammerse tend to sit against the AgileDesign principle.
AgileDesign principle benefits:
- Uses Generalization and the resulting Abstractions as a technique to make things common.
- Utilizes Design Patterns that facilitate change and extension
- Allows change to occur without affecting the core architecture, structure or pattern.
- Separates areas that change and those that don't into distinct artifacts. (example: Application and Framework or Client and Library)
- Utilizes White-Box and Black-Box designs effectively.
- Facilitates the other design principles.
AgileDesign side-effects:
- Duration of development can increase
- Configuration settings and metadata can increase
- More abstraction can lead to harder to read code
- Paper specifications are sometimes needed to clarify the gaps caused by abstract design
- Team must conform more to team principles
- Object-Oriented code can increase
- The next project has a platform on which to build, cause time decreases in other projects that use the same code base (depends on amount of reuse and effectiveness of the design
Intent
In the business of software, where milestones are king, and the quicker the better philosophy is all that matters, doing more agile things in our software is harder. It takes time to do things properly.
The vast majority of the developers that I have met in the community understands Design Patterns and will recite the benefits to you, from their reading, but will implement very little of it within their code.
There are various reasons why this happens, thats beyond the scope of this paper, however, its important to note, that AgileDesign is a mindset, not Design Patterns, but it does involve thinking and implementing a few Patterns to make things more Agile.
The intent of AgileDesign is to provide a small subset of Design Patterns, Design Principles, Design methodology and process, but especially the Design thinking, into a small easy to understand and importantly easy to follow guide.
Motivation
You are in the same predicament, as you have been many times before. You have been tasked with developing something quickly and you have a mixture of requirements. At first you are eager to get stuck in, but you remember that just rushing in, is not really a good thing, but likewise, you also don't want to appear as some sort of academic, who thinks about the problem, more than getting anything done.
There are many tools and you understand a lot of them, but perhaps the project is too small for you to start designing all the classes upfront, or perhaps its too large as it would take longer to create the UML for it than coding the actual thing.
The motivations are actually quite vast, here are a few.
- You have a complex domain model or set of requirements that can change
- There is too much information/data which needs a level of abstraction
- You know that this is the core of whole system and needs to be malleable
- You know you will need a higher percentage of reuse.
- You need to discover and separate reusable common elements from fixed static elements.
- Designing everything upfront is ridiculous to you
- Designing it now would take too much time.
- You are concerned/unsure about some areas in the project
- You have used a few Design patterns before, but not entirely sure of which one when and why
-
- You either don't think in OO, or you feel the project does not need it
- You are unsure of requirements, and/or architecture
Applicability
Portions of AgileDesign should be done upfront and portions should be done during the life-cycle at each point where you are faced with decisions.
During the explanations of various methods below, those that are probably more inclined to be at the early stage will be specially marked with [FS] = First Stage
Solution
- [FS]Infrastructure: Where will it live? One machine, access Web Service? Any special requirements?
- Paper: Draw borders around processes. Draw some objects down, what are you dealing with?
- [FS] Prototype: Don't spend time thinking up module names and the like, Any project names, create projects, delete them, rearrange them, rename, add classes.
- Generalization: Where is the Framework? Where is the Application? Where are the Extension Points? (separate by 'rate of change'. It is not always black or white, as with many things, certain aspects of the project will change more slowly than others. Separate code into separate Libraries, code files, classes, where the rate of change differs.
Solution: Apply Generalization
A very simple way of categorizing and making things common is to pose 2 questions at the problem. This is important to do every time you are faced with decisions on how to create some classes or faced with lots of things to consider.
- What is common?
- Is there a standard generalization that can be applied to make them even more common?
For example: you have the following problem.
In the first case "What is common?", we can revise the image, by what is common. As you can see (without even really thinking).
Result! We have some common elements.
However we could do this further with the second question, which will apply generalization.
The generalization rule is simple
"A Cat is an Animal." But an Animal is not always a Cat.
To apply generalization, we can say that these are all shapes. To test it, a Square is a Shape, but not all shapes are Square. Result!
When you apply this, it will give you some insight into your class design and possibly what Pattern to use. (See the next section)
Solution: Apply Design Patterns
When you apply generalization, it very quickly releases your Design Pattern participants. Not always, but often it results in understanding your abstract class from your concrete implementation.
For example, the Composite Pattern (GoF) now has a superclass (the abstract shape class) and concrete implementations (square, rectangle and circle).
The Design Pattern that will be decided on, may be a coalescent design pattern. ie. Consisting of 2 or more. You may have a Composite Pattern (GoF) with a FactoryMethod (GoF).
Another simple example would be given a context, which shape would be created using the FatoryMethod (GoF).
GoF Design Patterns I believe should always be considered within this stage are Factory method, Abstract Factory, Adaptor, Composite.
Solution: Design for Change
How much of this can you see changing? For example, it would be laughable to think that a square, rectangle and circle are the only shapes you will have to deal with. You will need to write the code so it accepts all types of shapes, but limited to these few (for now). Sometimes understanding that is easy and sometimes its harder. However you can easily apply refactoring later, if you discover something that will change, while you are busy.
The way you deal with change, is to create an Extension point (see ExtensibleDesign), for the thing that is expected to change. This will require you to use a Design Pattern that facilitates it.
Solution: Fine Grained
Break things down to their smallest assets/units, solutions, assemblies, projects as well as objects. Fine grained Assets provides lots of smaller objects, and can be overwhelming. However our tools will get better at helping us deal with fine grained objects.
When objects are smaller, they tend to manage less and do less. This means that the objects tend to become service oriented. Each object providing a service to another. A good combination of service and object-oriented.
Read Evolving Frameworks, by
Don Roberts and Ralph Johnson, although its only regarding objects, you can read the forces that are involved with designing in this way.
Solution: Dependency Injection
Utilize Constructor, Interface and Property Dependency Injection, in carefully constructed ways. Your designs can make use of objects and methods provided to you, rather than an explicit link or reference. Creating dependencies on Interface or interface rather than purely on a specifically Typed object can allow a good extension point.
Please read
Designing Reusable Classes for further thoughts.
Applicable Patterns
Abstract Factory (GoF)
Adaptor Factory (GoF)
Factory Method (GoF)
Builder (GoF)
Template Method
Adaptor (GoF)
Bridge (GoF)
Mediator
Composite (GoF)
Decorator (GoF)
Proxy (GoF)
Chain of Responsibility (GoF)
Visitor (GoF)
Creator (Grasp)
Low Coupling (Grasp)
Polymorphism (Grasp)
Indirection (Grasp)
The Single Responsibility Principle (SOLID)
The Open Closed Principle (SOLID)
Sprints (SCRUM)
Pair Programming (XP)
Test Driven (XP)
Feedback (XP)
Embracing change (XP)
Metamorphosis (Foote/Yoder)
ConflictsMinimalDesign, ReachableDesign
Ammerse Principles