This is the 14th day of my participation in Gwen Challenge

Design patterns

We have already covered two design principles in Design Pattern 1, which we will continue to cover.

Rely on the inversion principle

Relying on abstraction rather than concrete, the core idea is interface programming. The goal is good specification (design), not implementation.

For example, the code we’re writing now needs to be automatically deployed to the server, so start using Github for automated deployment. The implementation process is programmer -> submit code to Github -> automated deployment.

Programmer –> Submit code

Github –> Automated deployment

The wrong sample

package com.wangscaler.dependenceinversion;

/ * * *@author wangscaler
 * @date2021.06.16 17:43 * /
public class DependenceInversionPrinciple {
    public static void main(String[] args) {
        Programmer programmer = new Programmer();
        programmer.commit(new Github());
    }

    static class Github {
        public String cicd(a) {
            return "Github automation deployment complete"; }}static class Programmer {
        public void commit(Github github) { System.out.println(github.cicd()); }}}Copy the code

One day, github repository access was so slow that we stopped using it and replaced it with GitLab. At this point, we created a new GitLab repository and added the CICD method. However, although we had the repository, we could not automate the deployment because our programmers only knew github. At this time, Programmer relies on Github, which is unreasonable. The coupling degree between modules is too high and the productivity is too low.

The correct sample

package com.wangscaler.dependenceinversion;

/ * * *@author wangscaler
 * @date2021.06.16 17:43 * /
public class DependenceInversionPrinciple1 {
    public static void main(String[] args) {
        Programmer programmer = new Programmer();
        programmer.commit(new Gitlab());
    }

    static class Github implements IWarehouse {
        public String cicd(a) {
            return "Github automation deployment complete"; }}static class Gitlab implements IWarehouse {
        public String cicd(a) {
            return "Gitlab automation deployment completed"; }}public interface IWarehouse {
        public String cicd(a);
    }

    static class Programmer {
        public void commit(IWarehouse warehouse) { System.out.println(warehouse.cicd()); }}}Copy the code

Since github and GitLab both belong to the repository and have cicD methods, it’s just a matter of defining the repository interface and having them implement it. If later, and want to change huawei cloud warehouse, only need to add huawei cloud this class on the line.

package com.wangscaler.dependenceinversion;

/ * * *@author wangscaler
 * @date2021.06.16 17:43 * /
public class DependenceInversionPrinciple1 {
    public static void main(String[] args) {
        Programmer programmer = new Programmer();
        programmer.commit(new Huawei());
    }

    static class Github implements IWarehouse {
        public String cicd(a) {
            return "Github automation deployment complete"; }}static class Gitlab implements IWarehouse {
        public String cicd(a) {
            return "Gitlab automation deployment completed"; }}static class Huawei implements IWarehouse {
        public String cicd(a) {
            return "Automated deployment of Huawei Cloud warehouse completed"; }}public interface IWarehouse {
        public String cicd(a);
    }

    static class Programmer {
        public void commit(IWarehouse warehouse) { System.out.println(warehouse.cicd()); }}}Copy the code

Conclusion: According to the dependency inversion principle, we focus on the abstract rather than the concrete. In this case, the design idea should be programmer –> submit code to repository –> automate deployment. Whether it’s Github, GitLab, or Huawei Cloud, they all abstracted as repositories, starting with the underlying module Github. Imagine what it could abstract. This abstract class acts as a buffer layer, enhancing the extensibility of the code.

Richter’s substitution principle

All references to the base class can transparently use its subclasses. One of the basic principles of object-oriented design. Wherever a base class can appear, subclasses can, and derived classes can add new behavior to the base class. Reduces program portability and increases program coupling (changes to the base class affect all subclasses). When a subclass inherits from a parent class, try not to override the methods of the parent class.

For example, we have two birds, swallow and kiwi (can’t fly), originally our common birds are flying, so the code is written like this

package com.wangscaler.liskovsubstitution;
/ * * *@author wangscaler
 * @date2021.06.17 * /
public class LiskovSubstitutionPrinciple {
    public static void main(String[] args) {
        Swallow swallow = new Swallow();
        Kiwi kiwi = new Kiwi();
        swallow.setSpeed(110);
        kiwi.setSpeed(120);
        System.out.println(swallow.getFlyTime(240));
        System.out.println(kiwi.getFlyTime(240));
    }

    static class Bird {
        double speed;

        public void setSpeed(double speed) {
            this.speed = speed;
        }

        public double getFlyTime(double distance) {
            return(distance / speed); }}static class Swallow extends Bird {}static class Kiwi extends Bird {
        @Override
        public void setSpeed(double speed) {
            speed = 0; }}}Copy the code

The execution result

2.1818181818181817
Infinity
Copy the code

Because kiwi cannot fly, we rewrite the method of the parent class, causing us to set the speed in the parent class. It is taken for granted that the speed in kiwi is also set in the same way, which eventually leads to the error. We should set the more basic parent class, to avoid rewriting the method of the parent class when the word class inherits.

package com.wangscaler.liskovsubstitution;

/ * * *@author wangscaler
 * @date2021.06.17 * /
public class LiskovSubstitutionPrinciple {
    public static void main(String[] args) {
        Swallow swallow = new Swallow();
        Kiwi kiwi = new Kiwi();
        swallow.setFlySpeed(110);
        kiwi.setSpeed(120);
        System.out.println(swallow.getFlyTime(240));
        System.out.println(kiwi.getTime(240));
    }

    static class Animal {
        double speed;

        public void setSpeed(double speed) {
            this.speed = speed;
        }

        public double getTime(double distance) {
            return(distance / speed); }}static class Bird extends Animal {
        double flyspeed;

        public void setFlySpeed(double speed) {
            this.flyspeed = speed;
        }

        public double getFlyTime(double distance) {
            return(distance / flyspeed); }}static class Swallow extends Bird {}static class Kiwi extends Animal {}}Copy the code

Conclusion: The Richter substitution principle requires us to integrate base classes, try not to rewrite the parent class, can add functionality. If you have to override a parent class method, make sure that the input criteria are looser and the output criteria more stringent than the parent class. For example, in the original code, the input condition of the parent Bird class is range, and the input condition of the subclass Kiwi class is 0, which is obviously not in accordance with the rules.