I have been putting some effort into trying to improve my OOAD (Object Oriented Analysis and Design) knowledge recently. Instead of writing a huge detailed blog post, I am going to brain dump some of my "notes" — or rather most important takeaways from my recent studies onto here.
Hopefully this will be helpful for anyone else. ????
The General Software Design Process
- Gather requirements from the client.
- What does the client want their software in which you will create, do?
- Create use cases and scenarios.
- Form "happy cases".
- These are cases which will occur often and are the ones that should be handled in a general manner.
- Form "alternate cases".
- These are cases which occur only during certain states and consequences of actions.
- Generally less common than the "happy case".
- Form as many "corner cases" as possible.
- These are the unusual cases that can likely occur due to valid input — but the combinations of the input can result in something that is acceptable, but really isn't expected.
- Since these cases are unusual, and the input that causes these cases to appear are rare, don’t worry when it comes to trying to get "complete coverage". You'll fish out most of them during implementation and testing.
- Form "happy cases".
- Come up with a list of needed classes and properties at a very high level.
- Make a list of the obvious. These are the things that your program absolute must have in order to function at a basic level.
- Consider SRP: Single Responsibility Principle
- Your classes and objects should only try to do one thing, and do it well.
- If you find that you are rarely using a certain method or property, then it might not be in the appropriate class -- it may need to be moved away, or removed entirely!
- Design the architecture.
- Remember to design things in a loosely coupled manner.
- You should be able to add features easily, but never have to change any existing ones!
- DRY: Don't Repeat Yourself
- Use as much common code as possible. Program in such a manner to not have to duplicate code.
- It will be tough to figure out where to put a shared piece of code in, but try to think harder about it and refactor when necessary.
- See what you really gain from inheriting from a parent class.
- If you have a child class that at first, seems to be related to a parent class, and find yourself defining methods similar to the parent class but with different implementations, you might not be using inheritance correctly.
- Adhere to Liskov Substitution Principle (LSP)
- When you inherit from a class, the child class should be able to call parent class's methods without any special consideration.
- Code to an interface, not an implementation.
- We hear this one quite often... but what does it mean? Be as general as possible. Things change over time, but that doesn't mean your class should. Provide a set of basic methods which can then be implemented by other classes specific to a problem domain.
- Thing in terms of having classes perform actions. These actions are your interface methods.
- Refactor, test and iterate!
- Sometimes our code can start to violate some of the above OOP guidelines, but that’s okay... REFACTOR to make the code conform!
- Test your code as often as possible.
- Software is only useful if the client finds it to be useful. If what you just built isn't suitable to their tastes, don't worry! Just go back and work on what can be done to make it right!
- Repeat for any new features!
General OOP: Useful Definitions
Abstract Class
Definition: An abstract class is a class that contains abstract methods which can be overridden with an implementation.
Basically, an abstract class provides a set of attributes that will give a class a type. Abstract classes cannot be instantiated. If we inherit from an abstract class, we must override the abstract methods. Use an abstract class if we want to provide some sort of inheritance relationship. Your child class will contain the properties of the abstract class and will be of that type.
Concrete Class
Definition: A class that has no abstract methods and has provided an implementation of all its declared methods.
Usually, a class that inherits an abstract class and overrides the abstract methods can be a concrete class.
Interface
Definition: An interface serves as a contract when another class is said to implement it. The interface contains method signatures that are declared, but not implemented. It is up to the class which implements the interface to provide implementation of these methods.
Interfaces are particularly useful when we encounter a set of classes that need to have some action invoked that contain the common method signature. We can easily expect to call the method names due to knowing that they have stayed true to the contract by implementing an interface.
Polymorphism
Definition: If an object has used inheritance and interface implementation, chances are it can take many forms. The type can either be the declared class name itself, the parent class in which it inherits from, and in some languages, the interface it inherited from. All can be valid.
We can form a composition of classes and perform common operations if we use polymorphism to categorize our classes as a base type. We can also use polymorphic methods to dynamically call methods of objects which are instantiated from classes using a common interface.
Encapsulation
Definition; Hide as much as you can, only make known what you should be known. Methods and properties of classes that should not be used outside of itself and children should not be exposed to other classes to use.