The following subsections list the guidelines that are introduced in this book. Some guidelines are repeated in multiple chapters, as they can be applied in multiple contexts. A.1.1. Big Picture -
Don't Reinvent the Wheel -
Look for existing solutions to problems before creating new solutions (Chapter 2). -
Think About the Big Picture -
Decisions within a system should be congruent with the big picture (Chapter 3). -
Document Your Assumptions and Your Decisions -
Keep a journal for retrospectives (Chapter 3). -
Don't Repeat Yourself (DRY) -
Every piece of knowledge must have a single, unambiguous, authoritative representation within a system (Chapter 3). -
Plan Globally, Develop Locally -
Incremental implementations should fit into a global plan (Chapter 4). A.1.2. Extreme Abstraction -
Splitters Can Be Lumped More Easily Than Lumpers Can Be Split -
It is easier to combine two concepts than it is to separate them (Chapter 2). -
Clump Data So That There Is Less to Think About -
Clumping data cuts down on the number of concepts that have to be kept in mind (Chapter 2). -
When You're Abstract, Be Abstract All the Way -
Do not describe data items using primitive data types (Chapter 2). -
Strings Are More Than Just a String -
Treat String as a primitive data type. Describe attributes with abstract data types, instead of as String s (Chapter 2). -
Never Let a Constant Slip into Code -
Use a symbolic name for all values (Chapter 2). -
To Text or Not to Text -
Use text between programs, not within programs (Chapter 5). -
If It Has Collection Operations, Make It a Collection -
Collections separate object usage from object storage and hide implementation of aggregate operations (Chapter 5). -
Don't Change What It Is -
Create new terms rather than trying to apply new meanings to current terms (Chapter 12) A.1.3. Extreme Separation -
Adapt a Prefactoring Attitude -
Eliminate duplication before it occurs (Chapter 3). -
Don't Overclassify -
Separate concepts into different classes based on behavior, not on data (Chapter 5). -
Place Methods in Classes Based on What They Need -
If a method does not require instance data, it should not be a member of the class. Conversely, if all the method requires is the instance data, it should be a member of the class (Chapter 6). -
Honor the Class Maxims -
Make loosely coupled cohesive classes (Chapter 6). -
Do a Little Job Well and You May Be Called Upon Often -
Methods and classes that perform specific jobs can be reused more often (Chapter 6). -
Separate Policy from Implementation -
Keeping the what separated from the how makes the what more readable and maintainable (Chapter 6). -
Separate Concerns to Make Smaller Concerns -
Split responsibilities among multiple methods and multiple classes to simplify each method and class (Chapter 7). -
Test or Production; That Is the Question -
Place all test-only methods in a test interface (Chapter 8). -
Build Flexibility for Testing -
Plan for flexibility in your design to allow for ease of testing (Chapter 8). -
Decouple with Associations -
Association classes decouple the two classes being associated (Chapter 9). -
Split Interfaces -
Split a single interface into multiple interfaces if multiple clients use different portions of the interface (Chapter 10). -
Do a Little and Pass the Buck -
Add proxies to interfaces to add functionality (Chapter 11). -
Business Rules Are a Business unto Themselves -
Keep business rules separate from other logic (Chapter 13). A.1.4. Extreme Readability -
A Rose by Any Other Name Is Not a Rose -
Create a clearly defined name for each concept in a system (Chapter 2). -
Prototypes Are Worth a Thousand Words -
A picture of an interface, such as a screen, can be more powerful than just a description (Chapter 2). -
Communicate with Your Code -
Your code should communicate its purpose and intent (Chapter 3). -
Explicitness Beats Implicitness -
Being explicit reduces misinterpretation (Chapter 3). -
Declaration over Execution -
Declarative-style programming can provide flexibility without code changes (Chapter 5). -
Use the Same Layout to Get the Same Layout -
Use templates or scripts for classes and methods to create consistent logic (Chapter 10). -
The Easiest Code to Debug Is That Which Is Not Written -
Never write functionality that already exists in usable form (Chapter 11). -
Use the Client's Language -
Use the client's language in your code to make it easier to compare the logic in the code to the logic of the client (Chapter 13). A.1.5. Interfaces -
Create Interface Contracts -
Design with well-defined interfaces and enforce the contracts for those interfaces (Chapter 3). -
Validate, Validate, Validate -
At each interface, validate that the input matches the contract (Chapter 3). -
Test the Interface, Not the Implementation -
Use the contract of the interface to develop the functional tests, not the implementation behind it (Chapter 10). -
Adopt and Adapt -
Create the interface you desire and adapt the implementation to it (Chapter 11). -
Don't Let the Cold Air In -
With interfaces exposed to the outside world, ensure that input validation and logging occurs (Chapter 14). A.1.6. Error Handling -
Decide on a Strategy to Deal with Deviations and Errors -
Determine for your system what are deviations and what are errors, and how to deal with both (Chapter 3). -
Report Meaningful User Messages -
Error messages should be reported in the context of what the user can do about the error, instead of in terms of what the underlying error is (Chapter 3). -
Never Be Silent -
If a method encounters an error, it should report it, not remain silent (Chapter 6). -
Consider Failure an Expectation, Not an Exception -
Plan how operations should respond to failures (Chapter 13). A.1.7. General Issues -
Don't Speed Until You Know Where You Are Going -
Make the system right, before you make it fast (Chapter 3). -
The Spreadsheet Conundrum -
Recognize when you are making the row/column decision (Chapter 3). -
Consistency Is Simplicity -
A consistent approach to style and solutions can make code easier to maintain (Chapter 3). -
If It Can't Be Tested, Don't Require It -
Every functionality requirement, whether formally or informally stated, should have a test created for it. If you cannot test a requirement, there is no way to determine whether you have met it (Chapter 4). -
Plan for Testing -
Developing test strategies in advance can lead to a better design (Chapter 4). -
Figure Out How to Migrate Before You Migrate -
Considering the migration path might help you discover additional considerations in other areas of the design (Chapter 7). -
Know Who It Is -
Determine uniqueness criteria for objects that should be unique (Chapter 7). -
Perform a Retrospective After Each Release -
Examining your design and how you created it can help in the next release (Chapter 8). -
Nothing Is Perfect -
There is usually a better solution, but you can stop with good enough (Chapter 8). -
See What Condition Your Condition Is In -
Use state-based analysis to examine object behavior (Chapter 9). -
Get Something Working -
Create something basic before adding refinements (Chapter 10). -
Plan Your Logging Strategy -
Determine where and how you are going to log (Chapter 11). -
More Is Sometimes Less -
Use a prewritten module with more features than you currently need and adapt it to your current needs (Chapter 11). -
Be Ready to Import and Export -
Data should be available for use outside the system via a well-defined data interface (Chapter 12). -
Avoid Premature Generalization -
Solve the specific problem before making the solution general (Chapter 14). A.1.8. Security -
If You Forget Security, You're Not Secure -
Security should not be an afterthought. Consider it during all phases of development (Chapter 4). -
Consider Privacy -
Systems need to be designed with privacy in mind (Chapter 13). A.1.9. General Objects -
Avoid Premature Inheritance -
Inheritance needs time to evolve (Chapter 5). -
Think Interfaces, Not Inheritance -
Interfaces provide more fluidity in the relationships between classes (Chapter 6). -
Overloading Functions Can Become Overloading -
By using unique names , functions can be more self-describing (Chapter 6). -
When in Doubt, Indirect -
Indirection, using either methods or data, adds flexibility (Chapter 11). |