The Essence of Software Design
Effective Java, Important Design Patterns & Idioms
I - Creating and Destroying Objects
- Item 1: Consider providing static factory methods instead of constructors
- Item 2: Enforce the singleton property with a private constructor
- Item 3: Enforce noninstantiability with a private constructor
- Item 4: Avoid creating duplicate objects
- Item 5: Eliminate obsolte object references
- Item 6: Avoid finalizers
II - Methods common to all Objects
- Item 7: Obey the general contract when overriding equals
- Item 8: Always override hashCode when you overrride equals
- Item 9: Always override toString
- Item 10: Override clone judiciously
- Item 11: Consider implementing Comparable
III - Classes and Interfaces
- Item 12: Minimize the accessiblity of classes and members
- Item 13: Favor immutability
- Item 14: Favor composition over inheritance (Decorator-Pattern)
- Item 15: Design and document for inheritance or else prohbit it
- Item 16: Prefer interfaces to abstract classes
- Item 17: Use Interfaces only to design types
- Item 18: Favor static member classes over nonstatic
IV - Substitutes for C Constructs
- Item 19: Replace structures with classes
- Item 20: Replace unions with class hierarchies
- Item 21: Replace enum constructs with classes
- Item 22: Replace function pointers with classes and interfaces
V - Methods
- Item 23: Check parameters for validity
- Item 24: Make defensive copies when needed
- Item 25: Design method signatures carefully
- Item 26: Use overloading judiciously
- Item 27: Return zero-length arrays, not nulls
- Item 28: Write doc comments for al exposed API elements
VI - General Programming
- Item 29: Minimize the scope of local variables
- Item 30: Know and use the libraries
- Item 31: Avoid float and double if exact ansers are required
- Item 32: Avoid strings where other types are more appropirate (i.e. never use a string as an aggregate type)
- Item 33: Beware the performance of string concatenation
- Item 34: Refer to objects by their interfaces
- Item 35: Prefer interfaces to reflection
- Item 36: Use native methods judiciously
- Item 37: Optimize judiciously
- Item 38: Adhere to generally accepted naming conventions
VII - Exceptions
- Item 39: Use exceptions only for exceptional conditions
- Item 40: Use checked exceptions for recoverable conditions and run-time exceptions for programming errors
- Item 41: Avoid unnecessary use of checked exceptions
- Item 42: Favor the use of standarad exceptions
- Item 43: Throw exceptions appropriate to the abstraction
- Item 44: Document all exceptions thrown by each method
- Item 45: Include failure-capture information in detail messages
- Item 46: Strive for failure atomicity
- Item 47: Don't ignore exceptions
VIII - Threads
- Item 48: Synchronize access to shared mutable data
- Item 49: Avoid excessive synchronization
- Item 50: Never invoke wait outside a loop
- Item 51: Don't depend on the thread scheduler
- Item 52: Document thread safety
- Item 53: Avoid thread groups
IX - Serialization
- Item 54: Implement Serializable judiciously
- Item 55: Consider using a custom serialized form
- Item 56: Write readObject methods defnsively
- Item 57: Provide a readResolve method when necessary
- Abstracty Factory (Gamma, p. 87) - Provide an interface for creating families of related or dependent object without specifying their concrete classes.
- Builder (Gamma, p. 97) - Separate the construction of a complex object from its representation so that the same construction process can create different representations.
- Factory Methdod (Gamma, p. 107) - Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
- Prototype (Gamma, p. 117) - Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
- Singleton (Gamma, p. 127) - Ensure a class only has one instance, and provide a global point of access to it.
- Adapter (Gamma, p. 139) - Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.
- Bridge (Gamma, p. 151) - Decouple an abstraction from its implementation so that the two can vary independently.
- Composite (Gamma, p. 163) - Compose objects into tree structures to represent part-whole hierachies. Composite lets clients treat individual objects and compositions of objects uniformly.
- Decorator (Gamma, p. 175) - Attach additional responsiblities to an object dynamically. Decorators provide a flexible alternative to subclassing for exntending functionality.
- Facade (Gamma, p. 185) - Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.
- Flyweight (Gamma, p. 195) - Use sharing to support large numbers of fine-grained objects efficiently.
- Proxy (Gamma, p. 207) - Provide a surrogate or placeholder for another object to control access to it.
- Chain of Responsiblity (Gamma, p. 223) - Avoid coupling the sender of a request to its receiver by giving more than one object a change to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
- Command (Gamma, p. 233) - Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue of log requests, and support undoable operations.
- Interpreter (Gamma, p. 243) - Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.
- Iterator (Gamma, p. 257) - Provide a way to access the elements of an aggregate object sequentially without exposing its unterlying representation.
- Mediator (Gamma, p. 273) - Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from refering to each other explicitly, and it lets you vary their interaction independently.
- Memento (Gamma, p. 283) - Without violating encapsulation, capture and externalize an object's internal sate so that the object can be restored to this state later.
- Oberserver (Gamma, p. 293) - Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified an updated automatically.
- State (Gamma, p. 305) - Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
- Strategy (Gamma, p. 315) - Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm very independently from clients that use ist.
- Template Method (Gamma, p. 325) - Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Methode lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
- Visitor (Gamma, p. 331) - Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.
- Open-Closed-Principle (OCP, link) - A class should be open for extension but closed for modification.
- Liskov Substitution Principle (LSP, link) - An instance of a class should function as an instance of its superclass.
- Dependency Inversion Principle (DIP, link) - High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions.
DO NOT DEPEND ON A CONCRETE CLASS.
All relationsships in a program should terminate on an abstract class or an interface.
- Inversion of Control (IoC, link) - When using Frameworks the control flow is dictated by the framework and is the same for all applications.
- Interface Segregation Principle (ISP, link) - Clients should not be forced to depend on methods that they do not use. Classes that have "fat" interfaces can be broken up into groups of member functions and each one can serve a different set of requirements.
- Reuse/Release Equivalence Principle (REP, link) - The granule of reuse is the granule of release. Only components that are released through a tracking system can be effectively reused. This granule is the package.
- Common Reuse Principle (CRP, link) - The classes in a package are reused together. If you reuse one of the classes in a package, you reuse them all.
- Common Closure Principle (CCP, link) - The classes in a package should be closed together against the same kinds of changes. A change that affects a package affects all the classes in that package.
Balancing the dynamic forces
- Acyclic Dependencies Principle (ADP, link) - The dependency structure between packages must be a Directed Acyclic Graph (DAG). That is, there must be no cycles in the dependency structure.
- Stable-Dependencies Principle (SDP, link) - Depend in direction of stability. Depend always on something which is more stable than you are. The stability of a package refers to the amount of work required to make a change. Violations of the SDP can be removed by applying the DIP (e.g. using interfaces).
- Stable-Abstractions Principle (SAP, link) - A package should be as abstract as it is stable. Stable packages should also be abstract so that its stability does not prevent if from being extended. Instable packages should be conrete; it concrete code can be easily changed.
- Singe responsibility Principle (SRP, link) - Every object should have a single responsibility and all its services should be narowly aligned with that responsibility.