Follow SOLID Principals
SOLID principles are widely accepted set of rules that let you design/implement reusable and flexible Classes and methods. They consist of the following five principles.
Single Responsibility Principal – Promotes Encapsulation
A class/method should have a single, well-defined functionality. They should only contain code responsible to a single actor (of a use case).
Classes and Methods change because the requirements of the actors change. If a class / a method only contain code that is responsible for a single actor, they only have only one reason to change. This is important because different actors have different requirements, which evolves in different phases and for different reasons.
Open Closed Principal – Promotes Abstraction + Polymorphism
Classes (even Systems) you develop should be open for extension and closed for modification. New functionalities should introduce new codes to a system, not changes to the existing code. The modules you design should never change. When requirement changes you should extend and add more modules to change the behavior of the system.
This principle promotes Abstraction and Polymorphism. Your module should be designed on top of immutable abstractions so it never changes. However, anyone can implement different derivations of these abstractions to introduce new behaviors to the system.
Things that change for different reasons and in different rates, should exist in different places in the code. It is impossible to make a system 100% agnostic for all changes. The designer should find the most probable changes and should make the system to be resilient against them.
Find this very interesting paper on the topic – link.
Liskov Substitution Principal – Abstraction and Hierarchy
Any subtype should be directly substituted in place of a supertype reference. This means that a reference of the supertype should be replaceable with a subtype object without breaking the program. For this principle to work, it is essential that subtype does not change the behavior of supertype. The subtype should provide all the features of the supertype, then some extra
Find the research paper here.
Interface Segregation Principle
A client should not be forced to depend on the method that it does not need. This principle enforces splitting large interfaces to several small interfaces.
Dependency Inversion Principle
Abstract modules should contain high-level business logic. Concrete implementations should contain implementation details. When a high-level module needs to call a low-level module, define an interface on the side of the high-level module to invert the dependency.