A collection of patterns that you want to be aware of so as not to complicate the code

Introduction

Code that was initially simple is piled up. By adding or changing functions, fixing bugs, etc., the code gradually becomes complicated, leading to an increase in correction cost and a decrease in quality. This is quite natural for a product of a certain size, and you must be conscious of designing to prevent it from happening.

Here, we will introduce the methods and patterns that we are usually aware of so as not to complicate the code. (Large amount of DDD and Clean Architecture components) Also, the code that appears in this article is Java, but I think you can understand it to some extent without knowing Java.

Also, it's long because I wrote it in a wide range without being cohesive ... Since it is an anchor link for each item below, please only the item you are interested in.

-[Why getters / setters are not good](# why gettersetters are not good) -[Tell, Don't Ask!](#Don't ask the law of Demeter tell-dont-ask) -[Separation of concerns: Separation of Concerns (SoC)](# Separation of concerns --separation-of-concerns-soc) -[Dependencies and their directions](#Dependencies and their directions) -[Good DRY and bad DRY](#good dry and bad dry) -[Name important](# Name important)

Why getter / setter is not good

Getter where business logic is involved

The getter here refers to an instance variable that can be referenced from the outside. The reason why it is limited to "where business logic is involved" is that it may not be possible to deal with it unconditionally due to framework restrictions.

Classes using getter


public class Employee {
    private String name; //OK because it cannot be referenced from the outside
    private LocalDate contractDate;
    public int salary; //Public instance variables are prohibited because they can be referenced from the outside
    ...

    //Prohibition because instance variables can be referenced from the outside with getter
    public LocalDate getContractDate() {
        return this.contractDate;
    }
}

The process getContractDate in this example," getting the contract date of an employee, "has no business meaning. You don't know what you want to do by getting an employee's contract date. Is what has been achieved here to identify employees using the contract date? Is it a change in employee information due to the contract date? ?? And so on, this alone has become meaningless. This is synonymous with the use of the acquired data being thrown to the user side, which is a postponement of the design.

Now, let's take a look at the side that uses the class in which "design postponement" is occurring.

The side that uses the class in which "design postponement" occurs


int salary;
if (employee.getContractDate().isBefore(LocalDate.now())) {
    //Get paid for contracted employees(It does not include the day, but there are details...)
    salary = employee.salary;
}

As mentioned above, the business logic leaks to the class that wants to use ʻemployee. If a class that wants to use ʻemployee for the same purpose comes out in the future, similar copy code will be mass-produced everywhere. To prevent that, write the business logic directly in the class that has the data as follows.

Classes that prohibit getters


public class Employee {
    private LocalDate contractDate;
    private int salary;
    ...

    public int calculateSalary() {
        if (this.contractDate.isBefore(LocalDate.now())) {
            return this.salary;
        }
        return 0; //This way of returning is not good, but as an example...
    }
}

User class


int salary = employee.calculateSalary();

By stopping getter and writing business logic in the class that holds the data, you can prevent mass production of copy and paste code, and you will be able to know the business rules just by looking at the corresponding class.

Setter where business logic is involved

The setter here also refers to an instance variable whose value can be changed from the outside.

Class using setter


public class Employee {
    public LocalDate contractDate;  //Public instance variables can be assigned from the outside, so it is prohibited
    private int salary;
    ...

    //Prohibited because instance variables can be assigned externally with setter
    public void setSalary(int salary) {
        this.salary = salary;
    }
}

Again, the logic is setSalary, which has no business meaning of" changing employee salaries. " For example, it should be a method that expresses the business flow and rules, such as "I added it because I had good performance" and "I corrected it because I made a mistake in the input".

Also, the fact that the value may be changed by setter means that the state will change, so it is not safe. In particular, as the processing becomes deeper as shown below, the readability deteriorates because it exceeds the state that can be easily recognized by humans.

methodA(Employee employee)
  └ methodB(Employee employee)
    └ methodC(Employee employee)
      └ methodD(Employee employee) <-It is impossible if the employee status is rewritten here!Can't catch up!
        └ methodE(Employee employee)

Avoid setters with full constructors and immutable objects

Complete Constructor: Complete Constructor

A complete constructor means "determine the values of all instance variables in the constructor". Especially in the class dealing with business rules, it is guaranteed that the following state is created in the constructor.

--The values of all instance variables are determined --There is no contradiction as a business rule --The value of the instance variable is a computable valid value based on the range and business rules.

As a simple example, define a class that expresses the business rule that "employees always have a contract (there is a contract date), and they can contract up to 3 months in advance".

public class Employee {
    private LocalDate contractDate;

    public Employee(LocalDate contractDate) {
        if (contractDate == null) {
            throw new IllegalArgumentException("Contract date is required");
        }
        LocalDate currentDate = LocalDate.now();
        if (contractDate.isAfter(currentDate.plusMonths(3))) {
            throw new IllegalArgumentException("Contract dates over 3 months ahead are invalid");
        }
        this.contractDate = contractDate;
    }
}

By designing the constructor so that the contract date of the employee is always required and the date exceeding 3 months ahead is limited, the restrictions on the business rule are forcibly observed when using this instance. It also guarantees a secure instance. In addition, it becomes a safe and predictable instance no matter what value comes, and the user can use it without worrying about unnecessary things.

After instantiating once, operations such as changing the state or gradually assembling some fields reduce safety and readability. Instance generation should always be completed with an atomic operation.

Static factory method instead of constructor

Introducing static factory methods in relation to constructors. You may want to create multiple constructors that are overloaded in terms of business rules. As written in the comment of the example below, it feels like "this constructor for XX" and "this constructor for △△".

public class SearchDateTime {
   private LocalDate date;
   private LocalTime time;

    //I want you to use this constructor except on the day...
    public SearchDateTime(LocalDate date, LocalTime time) {
        this.date = date;
        this.time = time;
    }

    //I want you to use the constructor only on the day...
    public SearchDateTime() {
        this.date = LocalDate.now();
        this.time = LocalTime.now();
        //It doesn't matter, but let's chain the constructor!
        // this(LocalDate.now(), LocalTime.now());
    }
}

If there are multiple constructors for the above, it is written in the comment, but it is difficult for the user to understand which one to use just by the difference in the arguments.

The solution is the title "Static factory method instead of constructor". Prepare a static factory method named for each usage as shown below.

public class SearchDateTime {
   private LocalDate date;
   private LocalTime time;

    public static SearchDateTime of(LocalDate date, LocalTime time) {
        return new SearchDateTime(date, time);
    }

    public static SearchDateTime ofToday() {
        return new SearchDateTime(LocalDate.now(), LocalTime.now());
    }

    //Constructors are made private and not exposed to the outside world
    private SearchDateTime(LocalDate date, LocalTime time) {
        this.date = date;
        this.time = time;
    }
}

The original constructor reduces visibility to private and restricts it from being used directly from the outside. The advantage of static factory methods is that they can be ** named **. By making the name explicit, you can convey your intention to the user without making it ambiguous. Also, as another use case example, it is effective to limit the objects to be operated by the administrator role and the normal role, and to clarify the operations that should not be mistaken by the naming and the argument type.

Immutable object

You can increase safety by ensuring that the value is not changed once it is fixed. By defining the final modifier on an instance variable, the value will not change after initialization in the constructor.

public class Employee {
    //Add final modifier to all instance variables
    private final String name;
    private final LocalDate contractDate;
    private final int salary;

    public Employee(String name, LocalDate contractDate, int salary) {
        this.name = name;
        this.contractDate = contractDate;
        this.salary = salary;
    }

    public void addSalary(int salary) {
        this.salary += salary //Compile error!!
    }
}

The main purpose of quitting setter is to make it a complete constructor and immutable object so that you don't have to worry about state changes. Since the amount of information that a person can temporarily remember is small, it is important to reduce the amount of concern as much as possible.

Now you have an immutable object with an immutable value, but what if you want to change the value is to create a new instance and return it. It does not change its own value.

public class Employee {
    private final String name;
    private final LocalDate contractDate;
    private final int salary;
    ...

    //Re-contract and change contract date and salary
    public Employee contractRenewal(int salary) {
        return new Employee(this.name, LocalDate.now(), salary);
    }
}

//User side
Employee employee = new Employee(...);
Employee extendedEmployee = employee.contractRenewal(200000); //Treat as a separate instance.

Do not ask Demeter's law (Tell, Don't Ask!)

"The Law of Demeter" and "Don't Ask" are both design principles related to information hiding. First, let's take a look at an image of an example in which this design principle is not adhered to and an example in which it is adhered to.

Examples of non-observance of design principles

--Service is inquiring --References to objects other than those owned by Service (ModelB, ModelC)

No good.png

Examples of adherence to design principles

--I'm ordering --Service does not reference Model B and Model C

good.png

You can see that if you follow this design principle, you will get a beautiful V-shape.

Next, let's look at the code. As a simple example, it shows the logic of "finding the selling price of a product with a coupon fee".

Don't ask Demeter's Law, the command isn't followed User logic


Product product = new Product(something);
int sellingPrice;
if (product.price.couponExpiration.isAfore(LocalDate.now())) {
    //Returns the coupon price of the item if the coupon is valid(This also does not include the day, but there are details...)
    sellingPrice = product.price.couponValue;
} else {
    //Return product price if coupon expiration date is invalid
    sellingPrice = product.price.value;
}

In the above, an inquiry by if statement is generated in product.price.couponExpiration to determine the expiration date of the coupon. We are also accessing the price owned by the product.

The code improved by applying "Demeter's Law" and "Don't Ask" to this code is as follows.

Demeter's Law and Don't Ask, User Logic


Product product = new Product(something);
int sellingPrice = product.sellingPrice();

The above "Law of Demeter and user-side logic that keeps the command not to ask" is completed with one line of instruction. The user is given only ** minimum knowledge ** and only needs to call the command "find the selling price".

The Product class that incorporates this" Law of Demeter and tell me not to ask "looks like this:

public class Product {
    private Price price;
    ...
    int sellingPrice() {
      return price.sellingPrice();
    }
}

class Price {
    private int value;
    private int couponValue;
    private LocalDate couponExpiration;
    ...
    int sellingPrice() {
        if expirationDate.isAfore(LocalDate.now()) {
            return couponValue;
        }
        return value;
    }
}

Originally, the Price class is in charge of the judgment processing such as branching that was on the user side, and it is composed in the Product class. Business logic such as calculation and judgment is ** handled by the class that has the data **. In addition, the Price class is defined as package-private, and the sellingPrice method is package-private as well. In other words, this class and method cannot be seen from the user side (assuming another package), and direct execution is not permitted. This will force you to reduce ** knowledge exposure **, reduce dependence, and loosely couple.

According to Demeter's law, this is the image. product.png

Separation of concerns: Separation of Concerns (SoC)

Separation of concerns is often talked about in MVC and layered architecture, but it's a principle you want to be aware of, no matter what the particle size. For example, suppose you have a class that deals with "employees". In this "employee" class, if a business error occurs, "how to notify" is not a concern. If it is an API, the message should be packed in a JSON-like format, and if it is a website that displays a screen, it should be modified so that it can be easily recognized by the user. In this "employee" class, "a business error has occurred" and "the content of the business error (error message)" are the concerns that you should know. How to output it and how to qualify it is a concern of another class (eg Presentation layer). If the user's screen processing is written in the "employee" class without being aware of the separation of concerns, the "employee" class should not be relevant if the screen output method is changed. Will also be fixed, expanding the scope of impact. It also violates one of the SOLID principles, the ** single-responsibility principle **. https://qiita.com/UWControl/items/98671f53120ae47ff93a

By separating interests according to roles in this way, defining and demarcating the boundaries of responsibility, it is possible to design loosely coupled and secure applications with less dependence on the outside. Is possible. It also leads to a reduction in ** knowledge exposure **.

Dependencies and their directions

TL;DR Because it's a little long.

--Prevent circular dependencies --Rely on more stable components --The less dependents, the more stable, or the more dependent you are, the more stable --Don't rely on stable components, as the impact of changes will be greater if unstable components are dependent on them. --Reverse the dependence direction to solve them

Principles dealing with component associations

A "component" is a module, or a unit of jar or gem. However, it should be applied not only to components but also to any object. Therefore, the explanation here is based on the class.

** Acyclic Dependencies Principle (ADP) **

The component dependency graph must not have a circular dependency

There is a principle. It should be designed so that circular dependencies do not occur in any structure, not just "components". Make sure that the dependencies are inward and depend only on one direction without circulation. It is important not to know the outside from the inside and not to bring the knowledge and rules of the outside into the inside.

** Principle of Stable Dependencies Principle (SDP) **

Depends on the direction of high stability

In terms of classes, the fewer classes that depend on it, the more stable it is. It is also stable enough to depend on its own class. Especially for the latter reason (the more stable it depends on its own class), the effect of the change is large and it is difficult to change, so it has to be stable. (Depending on the language, I think the number of imports, require, and include will be a certain standard.)

Therefore, a class that is supposed to change should not be dependent on a class that is difficult to change.

** Principle of stability and abstraction equivalence: Stable Abstractions Principle (SAP) **

The abstraction of a component must be as stable as its stability

The principle is that a highly stable component should also be highly abstract, and that high stability should not interfere with expansion. Conversely, less stable components should be more specific. This is because the low stability makes it easier to change the specific code inside it.

The "Principle of Stability Dependence (SDP)" should depend on the direction of higher stability, and the "Principle of Stability / Abstraction Equivalence (SAP)" means that stability should be proportional to the degree of abstraction. In other words, ** should depend on the direction of higher abstraction **.

Dependency Inversion Principle (DIP)

In order to realize the "Principle of Stability Dependency (SDP)" and "Principle of Stability and Abstraction Equivalence (SAP)", it is necessary to change the direction of the dependency. By introducing an interface as a way to do this, we can reverse the direction of dependencies and achieve this.

Let's take a look before applying DIP. before_dependency_inversion_principle.png In the above "before change", the "stable class" depends on the "unstable class with many modifications", so if there is a change in the latter class, it will affect the former class. .. Specifically, the following is an example.

--If you change the return value of a method that "unstable class with many modifications" exposes to the outside, you must also modify "stable class". --If an "unstable class with many modifications" is making an API call, you must also modify the "stable class" if you replace it with another API.

Here is a state where the dependency is reversed and the problem is solved for such a problem.

dependency_inversion_principle.png

By relying on abstraction using the interface, "unstable classes with many modifications" are no longer directly dependent. No matter how much you fix an "unstable class with a lot of modifications", you don't need to modify a "stable class" as long as you inherit the interface.

What is important here is also described in "Principle of Stability Dependence (SDP)" and "Principle of Stability and Abstraction Equivalence (SAP)", but the dependent abstract class must be extremely stable. not. Therefore, the design of this part is important.

When actually applying the Dependency Reversal Principle (DIP)

Personally, I don't think it is necessary to apply this principle of dependency reversal (DIP) at all costs. Especially in the case of a simple structure at present, it may be difficult to understand by including a one-layer abstract object.

As an actual concrete example, I think that there are many cases where you may think "I don't want to depend on this class" when reversing the dependency direction.

--There is a class that is not stable due to frequent specification changes or a lot of bugs, and there is another class that is affected by it. --You are using an external library such as the functions provided by the framework. --API access that changes frequently or is out of control such as external

As a project, you should decide the direction to some extent in advance, such as "how far can you be locked by the framework".

Good DRY and bad DRY

Generally, "duplicate is evil" in the source code. "Don't Repeat Yourself" This is the DRY principle.

For example, suppose you have multiple use cases with similar screen configurations. At the beginning of making, let's assume that the same processing is standardized to make it DRY. However, although there was no problem at that time, the requirements of each use case are different, so the if branch of each user has their own individual intentions in ** common logic **, and a few years later. Can turn into a bloated ** common logic ** full of judgment branches.

To avoid this, it is necessary to clarify the problem domain (domain) that the product deals with, which is the domain that the business is interested in. On top of that, you need to carefully determine whether it is ** accidentally duplicated ** or ** really duplicated **. In the former case, there is no need to make it DRY because it is only ** accidentally duplicated **.

This is a problem that is more likely to occur as the product becomes larger. In particular, it is important to note that the classes that handle business rules have names such as common and base.

Name important

For some reason, it is the ** most important matter ** for programming. The content about the name is mentioned in many other articles, so I will only give an overview, but I think the following books will be very helpful.

-Readable Code : The basics in the basics are systematized and verbalized in an easy-to-understand manner. -CODE COMPLETE: No other book has been so deeply digged with more than 10 items for naming (probably).

After that, I will summarize the naming of "the part that handles business rules".

--The package name / module name matches the name of the problem domain handled by the domain. --The class name matches the concerns you are dealing with in the problem domain --The method name is the behavior you want to realize and the knowledge you want to know. --The name is not generic, ambiguous, or has a broad meaning

In DDD, there are "strategic design" and "tactical design".

--Strategic design: core domain, bounded context, context map --Tactical design: Design patterns and architectural designs to actually realize business rules

The important thing is that you can't decide on a proper name without a strategic design. Since I mentioned it in the item of DRY, I will repost it, but what is the area that our business is interested in, and if there is something that can be divided within that area, the problem area (domain) handled by the product is described. The task of clarifying and deciding the package name and class name according to it is necessary for good naming.

Finally

It is important not to apply the methods and patterns described so far once, but to look back over and over again if they do not fit. At that time, I think that even if you think you wrote good code, if you look back at it later, it will be a bad code. However, with code that has been conscious of such a design, the modification cost should be much lower than before, and it should be easier to change. It's important to constantly improve and repeat small refactorings.

Recommended Posts

A collection of patterns that you want to be aware of so as not to complicate the code
I want to be aware of the contents of variables!
An example of a small work when you want to divide the definition value according to the environment but do not want to be aware of it
7 things I want you to keep so that it doesn't become a fucking code
To be aware of easy-to-read code
When you want to check whether the contents of a property can be converted to a specific type
A memo when you want to clear the time part of the calendar
The story of introducing Gradle as a retrofit to an existing system that did not manage packages
[Ruby] I want to make a program that displays today's day of the week!
Basic rules to be aware of to write easy-to-read code
Even in Java, I want to output true with a == 1 && a == 2 && a == 3 (gray magic that is not so much as black magic)
[Active Admin] I want to specify the scope of the collection to be displayed in select_box
When you want to change the wording to be displayed when making a select box from enum
A memo of the program that allows you to realize that the probability of dice rolling is about 1/6
Things to be aware of when writing code in Java
When you want to change the MySQL password of docker-compose
Object-oriented design that can be used when you want to return a response in form format
Wasteful processing of collections-I want to give you a chance to write good code. 5 [C # refactoring sample]
[Swift5] How to create a .gitignore file and the code that should be written by default
The story of Collectors.groupingBy that I want to keep for posterity
A story that could be implemented neatly by using polymorphism when you want to express two types of data in one table
Creating an ArrayList that allows you to throw in and retrieve the coordinates of a two-dimensional plane
Correspond to "error that basic authentication does not pass" in the test code "The story that could not be done"
[Java] You might be happy if the return value of a method that returns null is Optional <>
[Java] Equivalence comparison where beginners fail in string comparison. You may not even be aware of the mistake! ??
[RSpec] When you want to use the instance variable of the controller in the test [assigns is not recommended]
About the matter that tends to be confused with ARG of Dockerfile which is a multi-stage build
To not be a static uncle
A memo that was soberly addicted to the request of multipart / form-data
How to interact with a server that does not crash the app
A collection of phrases that impresses the "different feeling" of Java and JavaScript
To you who lament that the conversion of JODConverter + LibreOffice is slow
A memo that enabled VS Code + JUnit 5 to be used on Windows 10
[Swift] If you want to use a URL that includes Japanese, use addingPercentEncoding.
I want you to use Enum # name () for the Key of SharedPreference
What to do when you want to delete a migration file that is "NO FILE"
If you want to satisfy the test coverage of private methods in JUnit
How to create a route directly from the URL you want to specify + α
[Android] I want to create a ViewPager that can be used for tutorials
Think of RxJava as a library that makes asynchronous processing easier to write
You may not want to use the remove method in ArrayList very often
Summarize the life cycle of Java objects to be aware of in Android development
I want you to use Scala as Better Java for the time being
I want to recursively get the superclass and interface of a certain class
Try to save the data that can be read by JavaFX as PNG
[java tool] A useful tool when you want to send the ipmsg log of PC-A to the specified PC on a regular basis.
I want you to put the story that the error was solved when you stabbed the charger in the corner of your head
I want to find the MD5 checksum of a file in Java and get the result as a string in hexadecimal notation.
A story that people who did iOS solidly may be addicted to the implementation of Listener when moving to Android
I used Docker for my portfolio as a beginner, so I hope that even 1mm will be helpful to someone.