Here are some general reasons for re-engineering and the design patterns for solving them:
1. Create an object by specifying a class in the display
Specifying a class name when creating an object allows you to be constrained by a particular implementation rather than by a particular interface. This will make future changes more complicated. To avoid this situation, you should create objects indirectly.
Design mode: Abstract Factory, Factory Method, Prototype.
2. Reliance on Special operations
When you specify a special action for a request, the way the request is completed is fixed. To avoid writing requests to death, you will be able to easily change the method of responding to requests at compile time or run time.
Design mode: Chain of resposibility, Command.
3. Reliance on hardware and software platforms
External operating system interfaces and application interfaces (APIs) are different on different hardware and software platforms. Software that relies on a particular platform will be hard to port to other platforms, and even hard to keep up with updates on the local platform. Therefore, it is important to limit the platform dependencies when designing the system.
Design mode: Abstract Factory, Bridge.
4. Dependency on object representation or implementation
Customers who know how an object is represented, saved, positioned, or implemented may also need to change when an object is changed. Hiding this information from customers can prevent cascading changes.
Design mode: Abstract Factory, Bridge, Memento, Proxy.
5. Algorithm dependencies
Algorithms are often extended, optimized, and replaced when they are developed and reused. An object that relies on a particular algorithm has to change as the algorithm changes. So algorithms that are likely to change should be isolated.
Design Patterns: Builder, Iterator, strategy, Template Method, Visitor
6. Tight coupling
Tightly coupled classes are difficult to reuse in isolation because they are interdependent. Tightly coupled to create a single block of system, to change or delete a class, you have to understand and change many other classes, such a system is difficult to learn, transplant and maintain a dense collective.
Design mode: Abstract Factory, Command, facade, mediator, Observer, Chain of Responsibility.
7. Extending functionality by generating subclasses
It is often difficult to customize objects by defining subclasses. Each subclass has a fixed implementation overhead (initialization, terminating processing, and so on). Defining subclasses also requires a deep understanding of the parent class. For example, redefining an operation may require other actions to be redefined. An operation that is redefined may invoke inherited actions. And the subclass method will cause the class to explode, because even for a simple extension, you have to introduce many new subclasses.
The general object combination technique and the specific delegation technique are another flexible method for the behavior of the composite objects beyond inheritance. New functionality can be added to an application by combining existing objects in new ways, rather than by defining subclasses of existing classes. On the other hand, excessive use of object combinations can make the design difficult to understand. Many design patterns produce designs in which you can define a subclass and combine his instances with existing instances to introduce custom functionality.
Design mode: Bridge, Chain of Responsibility, Composite, Decorator, Observer, strategy.
8. Unable to easily modify the class
Sometimes you have to change a class that is difficult to modify. Maybe you need the source code without it (this is the case for a business class library), or any change to the class might require modifying many other subclasses that already exist. Design patterns provide a way to modify a class in these cases.
Design pattern: Adapter, Decorator, Visitor.
Article excerpt from: Design pattern (the basis of reusable object-oriented software)
General reasons leading to object-oriented software redesign