When the system interface needs to add new methods, I really regret not learning Java design patterns earlier

Suppose there is an interface in the system, and this interface has been implemented by 10 implementation classes. Suddenly one day, a new requirement comes, and 5 implementation classes need to implement the same method. Then you add the definition of this method to the interface and think everything is perfect.

After you add this method to the interface and 5 of the implementation classes, compile it. Not good, the other five implementation classes reported an error, and did not implement the newly added method. You must know that the method definition in the interface must be implemented in the implementation class, and it will not compile without one.

At this time, what the senior at the beginning of the development said to you suddenly sounded in your ears: "The gap between these implementations may become larger and larger in the future, and methods may be added to the interface, so be careful to leave holes."

picture

What are you doing now?

Assuming that the previous interface is like this, there are only two methods for eating and drinking.

public interface IUser {

    /**
     * 吃饭啊
     */
    void eat();

    /**
     * 喝水啊
     */
    void drink();
}

Now there are 5 powerful implementation classes, and a play() method needs to be added.

Now that this is the case, what should be done now.

Let's break the jar, let's go

No matter what the interface is or not, which implementation class needs to be added, just add it directly to that implementation class. The interface remains the same as before. There are still only two methods for eating and drinking, and the play method is directly added to 5 in the implementation class.

public class UserOne implements IUser{

    @Override
    public void eat() {
        System.out.println("吃饭");
    }

    @Override
    public void drink() {
        System.out.println("喝水");
    }
    
    public void play() {
        System.out.println("玩儿");
    }
}

Although it can be realized, it completely deviates from the original intention of designing the interface. It was originally built like a five-star hotel, but after the first floor was built, the upper part became a thatched cottage.

From then on, the interface is the interface, and the implementation class is the implementation class, basically it doesn't matter. The flexibility comes out, and you can directly add methods in whichever implementation class you want to add in the future.

picture

Can I add another interface?

It's still a bit of pursuit, can I add a new interface? The previous interface remains unchanged, and a new interface is created. In addition to the previous two methods, the play method is added to this interface.

In this way, a separate interface is created for those who need to implement the play method. Like the following  IUseris the previous interface. IUserExtendThe interface is newly added, and the play() method is added. The implementation class that needs to implement the play() method is changed to implement the new IUserExtendinterface. Only a few implementation relationships are changed. The changes are not very big, and I am satisfied.

picture

But the good times don’t last long. After a few days, a new method will be added. Assuming it is the one in the picture above  UserOneand  UserNinethe method needs to be added, what should I do?

picture

If God gives me another chance

If God gives me another chance to do it again, I will say to myself: "Don't mess around, look at the design pattern".

adapter pattern

The adapter pattern can be created by creating an adapter class that implements the interface and provides a default implementation, and then the existing implementation class can inherit the adapter class instead of directly implementing the interface. In this way, the existing implementation class does not need to be modified, but only needs to implement the new method in the implementation class that needs to override the new method.

picture

Isn't it necessary to add a play() method? No problem, just add it directly to the interface.

public interface IUser {
    void eat();
    void drink();
    void play();
} 

The adapter class is very important. It is an intermediate adaptation layer and an abstract class. Didn't the implementation class directly implements the interface class before, but now the adapter class implements the interface class, and the implementation class extends the adapter class.

In the adapter class, you can give each method a default implementation, and of course you can do nothing.

public abstract class UserAdapter implements IUser {
    @Override
    public void eat() {
        // 默认实现
    }

    @Override
    public void drink() {
        // 默认实现
    }

    @Override
    public void play() {
        // 默认实现
    }
}
public class UserNine extends UserAdapter {
    @Override
    public void eat() {
        System.out.println("吃饭");
    }

    @Override
    public void drink() {
        System.out.println("喝水");
    }

    @Override
    public void play() {
        System.out.println("玩儿");
    }
}

public class UserTen extends UserAdapter {
    @Override
    public void eat() {
        System.out.println("吃饭");
    }

    @Override
    public void drink() {
        System.out.println("喝水");
    }
}

Call method:

IUser userNine = new UserNine();
userNine.eat();
userNine.drink();
userNine.play();

IUser userTen = new UserTen();
userTen.eat();
userTen.drink();

In this way, add methods at will in the interface, then add the default implementation of the corresponding method in the adapter class, and finally add the corresponding personalized implementation in the implementation class that needs to implement the new method.

strategy pattern

The strategy pattern allows different behaviors to be performed according to different strategies. In this case, you can define the new method as a strategy interface, and then provide a different strategy for each implementing class that needs to implement the new method.

Change the interface to an abstract class, where the eat() and drink() methods remain unchanged, and you can do nothing, and you can customize whatever you want in the implementation class.

The play() method was added later, so we focus on the play() method. The strategy in the strategy mode is used in the play() method.

public abstract class AbstractUser {

    IPlayStrategy playStrategy;
  
   public void setPlayStrategy(IPlayStrategy playStrategy){
        this.playStrategy = playStrategy;
    }
  
    public void play(){
        playStrategy.play();
    }

    public void eat() {
        // 默认实现
    }

    public void drink() {
        // 默认实现
    } 
}

IPlayStrategyIt is a strategy interface. The strategy pattern is a pattern for behavior. Playing is a kind of behavior. Of course, you can treat all the methods to be added later as behaviors.

We set a strategy interface for the behavior of "playing", and then no matter what you play or how you play, you can implement this  IPlayStrategyinterface.

public interface IPlayStrategy {

    void play();
}

Then now make two implementation classes to realize two ways of playing.

Implementation of the first playable game

public class PlayGameStrategy implements IPlayStrategy{

    @Override
    public void play() {
        System.out.println("玩游戏");
    }
}

Second implementation of playing soccer

public class PlayFootballStrategy implements IPlayStrategy{
    @Override
    public void play() {
        System.out.println("玩儿足球");
    }
}

Then define  AbstractUserthe subclass

public class UserOne extends AbstractUser{
    @Override
    public void eat() {
        //自定义实现
    }

    @Override
    public void drink() {
        //自定义实现
    }
}

Call method:

public static void main(String[] args) {
  AbstractUser userOne = new UserOne();
  // 玩儿游戏
  userOne.setPlayStrategy(new PlayGameStrategy());
  userOne.play();
  // 玩儿足球
  userOne.setPlayStrategy(new PlayFootballStrategy());
  userOne.play();
}

The overall class diagram looks like this:

picture

at last

Through the adapter mode and the strategy mode, we can ensure that the specific implementation classes implement a common interface or inherit a common base class, and at the same time, when adding new functions (methods), we can ensure that the design is as clear as possible. Unlike the previous way of smashing cans and smashing, the interface and the implementation class are almost out of relationship. Each implementation class has its own play.

Guess you like

Origin blog.csdn.net/Javatutouhouduan/article/details/132061504