当前位置:网站首页>Sharing ideas for a quick switch to an underlying implementation

Sharing ideas for a quick switch to an underlying implementation

2022-06-26 12:46:00 When you come back

    The real scene is often like this , We respond to a need , There will soon be a solution , Then, a good implementation is made according to the requirements . Because the function is realized , The business is very happy, The boss is happy ,all the world is beatiful.

     But with the development of the company , Someone has implemented an underlying set of standard components , As required, you have to access his , His function is similar to yours , But you have to switch to that . Regardless of the quality of its implementation , But he certainly has some advantages , But as a standard kit , It can't be completely consistent with your needs . therefore , This must involve the problem of transformation .

     In this case , We are not willing to take it , After all, the code is running well , Who wants to move ? And how other people achieve , Not tested yet , Rash access , It may bring a larger pot .( from 0 To 1 Nobody cares about accuracy , But from 1 TripAdvisor 1.1 Someone will pay attention to accuracy , In other words, this is called compatibility )

     however , Often under pressure , We have to answer again .

     At this time, we have two ways , One is to directly change the code to someone else . This treatment is simple and rough , And there are no worries . however , And then , Is a large-scale regression test , And some points that may not be tested , Means code rollback . For some places where online operation and maintenance is more convenient , Maybe we can do this . But this is not the recommended practice in this article , No more discussion .

     A more prudent approach , It should be in the case of maintaining the existing implementation , Make the newly realized access , At least you can compare . Advance and attack , Retreat and defend .


 

1. Fast access to new implementations 1: abstract class

     Since we dare not directly replace the existing implementation , Then you have to keep two implementations , So you can use abstract classes , While maintaining the original implementation , Cut into new implementations . It's an intuitive idea , The specific implementation is as follows :

1.  Abstract a public class

 

public abstract class AbstractRedisOperate {

    private AbstractRedisOperate impl;

    public AbstractRedisOperate() {
        String strategy = "a";  // from config
        if("a".equals(strategy)) {
            impl = new RedisOperateA1Imp();
        }
        else {
            impl = new RedisOperateB2Imp();
        }
    }

    //  Example operation interface 
    public void set(String key, String value);
}

 

2. Implement two concrete classes

//  Realization 1, Completely dependent on abstract class implementation ( Old functions )
public class RedisOperateOldImp extends AbstractRedisOperate {

}

//  Realization 2, Implementation of new access 
public class RedisOperateB2Imp extends AbstractRedisOperate {

    @Override
    public void set(String key, String value) {
        System.out.println("this is b's implement...");
    }
}

 

3. Keep the original implementation class entry , Turn its implementation into a facade class or an adapter class

//  Loading entry 
@Service
public class RedisOperateFacade extends AbstractRedisOperate {

    public RedisOperateFacade() {
        // case1.  Leave it directly to the parent class 
        super();
    }

    @Override
    public void set(String key, String value) {
        // fake impl
    }
}

 

     What are the benefits of the above implementation ? First , Existing implementations are pulled away , And it was preserved without any changes . The new implementation class implements a new . Through a common changeover switch , Perform switching processing . thus , It can ensure the access to the new implementation , It also retains the old implementation , In the event of an unknown failure , Can be implemented by failback .

     What is the problem with the above implementation ?

     When we run the code above , I found the wrong report , Why? ? Because there is a dead circle . Although we only loaded one Facade The implementation of the , But in calling super when ,super The concrete implementation will be loaded in turn , The concrete implementation will load the abstract classes super, And so on and so on , Until the stack overflows . Also known as the emergence of a dead cycle .


 

2. Solve the problems caused by simple abstraction

     In the previous section, we already knew why the loading failed , In fact, it is a circular dependency problem . How to solve it ?

     It's simply moving the code , Don't put judgment in the default constructor , Handled by a concrete appearance class , The loading policy is determined by the appearance class , Instead of concrete implementation classes or abstract classes .

     The specific operation is as follows :

// 1.  Appearance class controls loading 
@Service
public class RedisOperateFacade extends AbstractRedisOperate {

    public RedisOperateFacade() {
        // case1.  Leave it directly to the parent class 
        // super();
        // case2.  Decide which implementation to load 
        String strategy = "a";  // from config center
        if("a".equals(strategy)) {
            setImpl(new RedisOperateOldImp());
        }
        else {
            setImpl(new RedisOperateB2Imp());
        }
    }

}
// 2.  Each implementation keeps itself still 
public class RedisOperateOldImp extends AbstractRedisOperate {
    // old impl...
}

public class RedisOperateB2Imp extends AbstractRedisOperate {

    // new impl...
    @Override
    public void set(String key, String value) {
        System.out.println("this is b's implement...");
    }
}

// 3.  Abstract classes are no longer subject to load policy processing 
public abstract class AbstractRedisOperate {
    //  Hold concrete realization 
    private AbstractRedisOperate impl;

    public AbstractRedisOperate() {
    }

    protected void setImpl(AbstractRedisOperate impl) {
        this.impl = impl;
    }

    //  Example operation interface , old impl...
    public abstract void set(String key, String value);
}

 

     Made minor changes , Transfer the loading policy from the abstract class to the appearance class , The correct loading effect can be achieved . actually , For the sake of simplicity , We can even make the original implementation complete copy Into abstract classes , And a new original implementation class , You don't have to do anything , Just add an empty inheritance abstract class . And the new implementation , Then it is enough to completely cover the existing concrete implementation . So as to achieve a minimum change , And smoothly access a newly implemented effect .

     But if it depends on the concrete implementation of the abstract class , There's a problem , That is, if our subclasses are not well implemented , For example, when some implementations are omitted , The code itself does not give an error message . This brings us potential risks , Because that would turn into , Part is the old implementation , The other part is the new implementation . There may be two problems : One is that one of the two implementations reports an error and the other is normal ; Second, rollback cannot be switched normally , The two implementations are coupled together .


 

3. A more perfect scheme : Different implementations based on interfaces

     What shall I do? ? We can abstract another layer of interfaces , Each implementation deals with the interface , Only the appearance class inherits the abstract class , And the abstract class also implements the interface definition . In this case , This ensures the integrity of each implementation , And the uniformity of appearance classes . here , I take advantage of the mandatory nature of grammar , That is, the semantics of the interface must be implemented , Ensure the accuracy of the code .( Yes, of course , All the real scenes , Interfaces must have corresponding implementations , Because only interfaces are visible from the outside , If not, it must be illegal )

The specific implementation is as follows :

//1.  Unified interface definition 
public interface UnifiedRedisOperate {

    void set(String key, String value, int ttl);

    // more interface definitions...
}
// 2.  Each sub implementation class 
public class RedisOperateOldImp implements UnifiedRedisOperate {

    @Override
    public void set(String key, String value) {
        System.out.println("this is a's implement...");
    }
}
public class RedisOperateB2Imp implements UnifiedRedisOperate {

    @Override
    public void set(String key, String value) {
        System.out.println("this is b's implement...");
    }
}
// 3.  Implementation of appearance class 
@Service
public class RedisOperateFacade extends AbstractRedisOperate {

    public RedisOperateFacade() {
        // case1.  Leave it directly to the parent class 
        // super();
        // case2.  Appearance class controls loading 
        String strategy = "a";  // from config center
        if("a".equals(strategy)) {
            setImpl(new RedisOperateOldImp());
        }
        else {
            setImpl(new RedisOperateB2Imp());
        }
    }

}
public abstract class AbstractRedisOperate implements UnifiedRedisOperate {

    private UnifiedRedisOperate impl;

    protected void setImpl(UnifiedRedisOperate impl) {
        this.impl = impl;
    }

    //  Interface delegation 
    public void set(String key, String value) {
        impl.set(key, value);
    }

    // more delegates...
}

 

     It seems that an additional interface class has been added , But actually the whole code is much clearer and easier to read . actually , A good design , Initially, it should also be based on interfaces ( Interface oriented programming ), Here we re abstract an interface class , In fact, it is to make up for the shortcomings of the previous design , It is also a kind of refactoring . All implementations are based on interfaces , None of them can be less , Thus reducing the probability of error .

     such , We can safely switch production .

  The article was originally published on wechat official account :  A quick switch to a low-level implementation of ideas to share

原网站

版权声明
本文为[When you come back]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/177/202206261141485636.html