Kaiser-Yang/matrix-calculation-accelerator

Refactoring and Design Patterns

Closed this issue · 0 comments

What is refactoring?

GPT can express this much better than I could, this below is come from a large language model:

Code refactoring is the process of restructuring existing code without changing its external behavior. The goal of refactoring is to improve the internal structure of the code, making it more readable, maintainable, and efficient.

Some common reasons for code refactoring include:

  1. Improving code readability and organization: Refactoring can involve renaming variables, methods, and classes to make the code more self-explanatory and easier to understand.
  2. Reducing code complexity: Complex code can be simplified by breaking it down into smaller, more manageable functions or classes.
  3. Eliminating duplication: Refactoring can help identify and remove duplicated code, which can improve maintainability and reduce the risk of bugs.
  4. Optimizing performance: Refactoring can help identify and address performance bottlenecks in the code.
  5. Preparing for future changes: Refactoring can make the code more flexible and adaptable, making it easier to add new features or make changes in the future.

Some common refactoring techniques include:

  • Extract Method: Extracting a block of code into a new method or function.
  • Rename Variable/Method/Class: Renaming a variable, method, or class to better reflect its purpose.
  • Introduce Polymorphism: Replacing conditional logic with polymorphic behavior.
  • Extract/Inline Variable: Extracting a temporary variable or inlining a variable.
  • Move Method/Field: Moving a method or field to a more appropriate class.
  • Decompose Conditional: Replacing complex conditional logic with multiple simpler conditions.
  • Effective code refactoring requires a deep understanding of the codebase, good coding practices, and a solid testing strategy to ensure that the refactored code still behaves as expected.

A take away message:

The most important things or the intentions of refactoring is to make the code more readable, maintainable, and efficient.

This refactoring of our code is to improve the readability and maintainability.

What are the problems of our code?

The most severe problem of our code is that there is too much code which has the same structure. In other words, there are many repetitions of our code. For example, before merging #109, all the constructors and copy assignments will have this code below:

if (shape.size() == 0) { return; }
_shape   = shape;
capacity = size();
data     = std::make_unique<ELEMENT_TYPE[]>(size());

This repetition may cause many problems:

  • When someone made a mistake, e.g: _shape = shape -> shape = _shape, it is hard to figure it out.
  • When find a mistake, and we need fix the mistake. It is very troublesome to fix (If there are 10 constructors, you must update 10 times).
  • This is readable but limited, when a developer looks at these four line, it may easily figure out that these four lines will allocate memory for the data. But why not make there only one line consisting of allocateMemory, which is more readable.
  • ...

Therefore, I've created a method called allocateMemeroy in Matrix to do the thing the four lines do:

template <class ELEMENT_TYPE>
inline void Matrix<ELEMENT_TYPE>::allocateMemory(const Shape &shape) {
    // no need to re-allocate
    if (size() == shape.size()) {
        _shape = shape;
        return;
    }
    _shape = shape;
    if (size() == 0) {
        _data = nullptr;
        return;
    }
    _data = std::make_unique<ELEMENT_TYPE[]>(size());
}

In all the constructors and copy assignments. The outer method only needs to call the allocateMemory to allocate memory.

After this process, if there were 10 allocations before, the code of allocation will drop by nearly 90%. Remember that more code may be not better.

Besides memory allocation, in our calculations (mca.h) and matrix's methods (matrix.h), there are many repetitions. Our objection is to make them more readable and maintainable.

What are design patterns?

Once we've noted the refactoring, we'll mention the design patterns. Let GPT explain what are design patterns:

Design patterns are common solutions to recurring design problems in software development. They are reusable templates or blueprints that provide a structured approach to solving specific types of problems.

Design patterns are classified into three main categories:

  1. Creational Patterns: These patterns deal with the creation of objects, providing ways to create objects in a manner suitable to the situation. Examples include Singleton, Factory, Abstract Factory, Builder, and Prototype.
  2. Structural Patterns: These patterns focus on the composition of classes or objects, providing ways to structure and organize complex systems. Examples include Adapter, Bridge, Composite, Decorator, Facade, Flyweight, and Proxy.
  3. Behavioral Patterns: These patterns deal with the interaction and communication between objects, providing solutions for effective object communication and behavior. Examples include Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, and Visitor.

Some of the most commonly used design patterns include:

  • Singleton: Ensures that a class has only one instance and provides a global point of access to it.
  • Factory Method: Defines an interface for creating an object, but lets subclasses decide which class to instantiate.
  • Decorator: Attaches additional responsibilities to an object dynamically, providing a flexible alternative to subclassing.
  • Observer: Defines a one-to-many dependency between objects, so that when one object changes state, all its dependents are notified and updated automatically.
  • Strategy: Defines a family of algorithms, encapsulates each one, and makes them interchangeable.

Design patterns provide several benefits, including:

  • Reusability: Patterns offer tested and proven solutions to common design problems, saving time and effort.
  • Flexibility: Patterns promote flexibility by creating loosely coupled designs that are easier to modify and extend.
  • Maintainability: Patterns improve code organization and readability, making the codebase more maintainable.
  • Communication: Patterns provide a common vocabulary for developers to discuss and describe design solutions.
    Understanding and applying design patterns can significantly improve the quality, flexibility, and maintainability of software systems.

Not too short, right? There is a take away message:

Design patterns are the conclusions of refactoring.

Learn more about design patterns from this book: Head First Design Patterns 2nd ed

How to refactor our codes?

I'll not give you any example. All you can do is try a team work with other developers.