Design mode - responsibility chain mode

brief introduction

  • Commons chain is a subproject of apache commons. It is mainly used in the "responsibility chain" scenario. The call process of action in struts is supported by the "chain" framework If your project also has requirements based on this scenario, you can consider using it
  • The so-called "responsibility chain" is a series of ordered command s that can be executed in order and can exchange or transmit execution results to each other, which is similar to the "responsibility chain" model we often call

Let's start with an example

  • Automobile sales process: test run - > sales negotiation - > arrange Finance - > end sales
    Suppose you use a traditional template pattern
public abstract class SellVehicleTemplate {  
   // Selling cars 
   public void sellVehicle() {  
        testDriveVehicle();  
        negotiateSale();  
        arrangeFinancing();  
        closeSale();  
    }  
    // Test run
    public abstract void testDriveVehicle();  
    // Sales negotiation
    public abstract void negotiateSale();  
    // Arrange Finance
    public abstract void arrangeFinancing();  
    // Close sales
    public abstract void closeSale();  
}  

Use the common chain responsibility chain mode

import org.apache.commons.chain.Command;  
import org.apache.commons.chain.Context;  

// Get customer information
public class GetCustomerInfo implements Command {
    public boolean execute(Context ctx) throws Exception {
        System.out.println("GetCustomerInfo ");
        return false;
    }
}

// Commissioning (Continued)
public class TestDriveVehicle implements Command {  
    public boolean execute(Context ctx) throws Exception {  
        System.out.println("Test drive the vehicle");  
        return false;  
    }  
}  
// Sales negotiation
public class NegotiateSale implements Command {  
    public boolean execute(Context ctx) throws Exception {  
        System.out.println("Negotiate sale");  
        return false;  
    }    
}  
// Arrange Finance
public class ArrangeFinancing implements Command {  
    public boolean execute(Context ctx) throws Exception {  
        System.out.println("Arrange financing");  
        return false;  
    }    
}  
// Close sales
public class CloseSale implements Command {  
      public boolean execute(Context ctx) throws Exception {  
        System.out.println("Congratulations " + ctx.get("customerName") +", you bought a new car!");  
        return false;  
    }  
}  

// Define responsibility chain and test
import org.apache.commons.chain.impl.ChainBase;  
import org.apache.commons.chain.Command;  
import org.apache.commons.chain.Context;  
import org.apache.commons.chain.impl.ContextBase;  
// Inherit ChainBase
public class SellVehicleChain extends ChainBase {  
  
    public SellVehicleChain() {  
        super();  
        addCommand(new GetCustomerInfo());  
        addCommand(new TestDriveVehicle());  
        addCommand(new NegotiateSale());  
        addCommand(new ArrangeFinancing());  
        addCommand(new CloseSale());  
    }  
  
    public static void main(String[] args) throws Exception {  
        Command process = new SellVehicleChain();  
        Context ctx = new ContextBase();  
        process.execute(ctx);  
    }  

// Operation results:
Test drive the vehicle 
Negotiate sale 
Arrange financing 
Congratulations George Burdell, you bought a new car! 

Context explicit call

  • Commons Chain provides a way to define the responsibility chain in the configuration file, and create chain - config. In the project resource directory XML file
    <catalog>  
      <chain name="sell-vehicle">  
        <command id="GetCustomerInfo" className="com.jadecove.chain.sample.GetCustomerInfo"/>  
        <command id="TestDriveVehicle" className="com.jadecove.chain.sample.TestDriveVehicle"/>  
        <command id="NegotiateSale" className="com.jadecove.chain.sample.NegotiateSale"/>  
        <command id="ArrangeFinancing" className="com.jadecove.chain.sample.ArrangeFinancing"/>  
        <command id="CloseSale" className="com.jadecove.chain.sample.CloseSale"/>  
      </chain>  
    </catalog>
    
    // Read from xml configuration
    public class CatalogLoader {  
      
        private static final String CONFIG_FILE = "/com/jadecove/chain/sample/chain-config.xml";  
      
        private ConfigParser parser;  
      
        private Catalog catalog;  
      
        public CatalogLoader() {  
            parser = new ConfigParser();  
        }  
      
        public Catalog getCatalog() throws Exception {  
            if (catalog == null) {  
                parser.parse(this.getClass().getResource(CONFIG_FILE));  
            }  
            catalog = CatalogFactoryBase.getInstance().getCatalog();  
            return catalog;  
        }  
      
        public static void main(String[] args) throws Exception {  
            CatalogLoader loader = new CatalogLoader();  
            Catalog sampleCatalog = loader.getCatalog();  
            Command command = sampleCatalog.getCommand("sell-vehicle");  
            Context ctx = new SellVehicleContext();  
            command.execute(ctx);  
        }  
    }  
    

    API details

  • Command: an executable "instruction", and multiple commands form a "responsibility chain". It has only one method, Boolean execute (context). When the "responsibility chain" starts calling, all command object execute methods on the chain will be executed successively until the end, or an exception is thrown, or a command returns true to terminate the call. The context object represents the context information of the current "responsibility chain", which can be used to save some temporary variables (which can be shared among commands). If this method returns false, it means that the responsibility chain will continue to execute subsequent commands. If it returns true, it means that the call is terminated and subsequent commands will not be executed
  • Chain: the implementation class is ChainBase, the command organizer, including addCommand method. You can add multiple commands, and then call the execute method to execute the execute method in the command chain in turn.
  • Filter: it also extends the command interface, can be added to the Chain like command, and has an additional interface Boolean postprocess (context, exception, exception). When all command and filter execute methods are executed without exception, the postprocess method of filter will be executed in reverse order; Once an exception occurs in the link, the postprocess method of the filter will be executed immediately. If the method returns true after processing, it indicates that the error processing has been completed and the execution of the Chain will not end. However, in essence, this error is caught and will not be thrown out. If the postprocess method returns false, the error will continue to be thrown out, and then the Chain will end abnormally.
  • Context: similar to "session", it is used to save the "variable parameters" or "Command execution results" that need to be maintained in the current Chain. The internal storage structure of context is a Map, and its data can be operated by the Command in the Chain. Context is not thread safe, and it is not expected that this instance can be reused. Every time the Chain is called, a new context instance should be created, Its implementation class is ContextBase.
  • We can write a SellVehicleContext class to inherit ContextBase and perform type conversion in command, so that we can get the value directly through get
public class SellVehicleContext extends ContextBase {  
  
    private String customerName;  

    public String getCustomerName() {  
        return customerName;  
    }  

    public void setCustomerName(String name) {  
        this.customerName = name;  
    }    
}  
// Type conversion
public boolean execute(Context ctx) throws Exception {  
    SellVehicleContext myCtx = (SellVehicleContext) ctx;  
    System.out.println("Congratulations " + myCtx.getCustomerName() + ", you bought a new car!");  
    return false;  
}  

// call
public static void main(String[] args) throws Exception {  
    Command process = new SellVehicleChain();  
    Context ctx = new SellVehicleContext();  
    process.execute(ctx);  
}  

Version used in work:

import org.apache.commons.chain.Context;
import org.apache.commons.chain.impl.ChainBase;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.stereotype.Component;
/**
 * Business code entries of different responsibility chains are distinguished by chainName
 */
@Component("chainRunner")
public class ChainRunner implements BeanFactoryAware {
	
	private BeanFactory beanFactory;
	
	public void runChain( String chainName,  Context context) {
		try {
			createChain(chainName).execute( context );
		}
		catch ( Exception exc ) {
			throw new RuntimeException(
					"Chain \"" + chainName + "\": Execution failed.", exc );
		}
	}
	
	public void setBeanFactory( BeanFactory beanFactory ) throws BeansException {
		this.beanFactory = beanFactory;
	}
 
	protected ChainBase createChain( String chainName ) {
		return ( ChainBase ) this.beanFactory.getBean( chainName );
	}
}

Cite the above example:

// Get customer information
public class GetCustomerInfoCommand implements Command {
    public boolean execute(Context ctx) throws Exception {
        System.out.println("GetCustomerInfo ");
        ctx.put("getCustomerInfo","ok");
        return false;
    }
}


// Commissioning (Continued)
@Component
public class TestDriveVehicleCommand implements Command {
    public boolean execute(Context ctx) throws Exception {
        System.out.println("Test drive the vehicle");
        ctx.put("testDriveVehicle","ok");
        return false;
    }
}  


// Sales negotiation
@Component
public class NegotiateSaleCommand implements Command {
    public boolean execute(Context ctx) throws Exception {
        System.out.println("Negotiate sale");
        ctx.put("negotiateSale","ok");
        return false;  
    }    
}  


// Arrange Finance
@Component
public class ArrangeFinancingCommand implements Command {
    public boolean execute(Context ctx) throws Exception {
        System.out.println("Arrange financing");
        ctx.put("arrangeFinancing","ok");
        return false;  
    }    
}  

// Close sales
@Component
public class CloseSaleCommand implements Command {
      public boolean execute(Context ctx) throws Exception {
        System.out.println("Congratulations " + ctx.get("customerName") +", you bought a new car!");  
        return false;  
    }  
}  

Test:

public class ChainCarSalesTest extends BaseTest {


    @Autowired
    private ChainRunner chainRunner;


    @Test
    public void test() {
        Context context = new ContextBase();
        context.put("customerName", "Lao Wang");
        chainRunner.runChain(ChainCarSales.SERVICE_NAME, context);
        // Get return value
        System.out.println("Get one of them command Return value of:"+context.get("negotiateSale"));
    }
}


GetCustomerInfo 
Test drive the vehicle
Negotiate sale
Arrange financing
Congratulations Lao Wang, you bought a new car!
Get one of them command Return value of:ok



Author: industrious bear
Link: https://www.jianshu.com/p/883bab2fcd36
Source: Jianshu
The copyright belongs to the author. For commercial reprint, please contact the author for authorization, and for non-commercial reprint, please indicate the source.

Keywords: Design Pattern

Added by bender on Sun, 19 Dec 2021 21:53:14 +0200