1. 程式人生 > >JStorm與Storm源碼分析(七)--BasicBoltExecutor與裝飾模式

JStorm與Storm源碼分析(七)--BasicBoltExecutor與裝飾模式

clean 規範 tco exe -418 orm框架 bsp ide per

在Storm中IBasicBolt的主要作用是為用戶提供一種更為簡單的Bolt編寫方式,更為簡單體現在Storm框架本身幫你處理了所發出消息的Ack、Fail和Anchor操作,而這部分操作是由執行器BasicBoltExecutor 實現的。
下面我們看一下BasicBoltExecutor的源碼:

/**
 * BasicBoltExecutor實現了IRichBolt接口
 * 在該類中持有一個IBasicBolt成員變量用於調用轉發
 * 說明:
 * 該類是基於裝飾模式實現的.
 */
public class BasicBoltExecutor implements IRichBolt {
    public static Logger LOG = LoggerFactory.getLogger(BasicBoltExecutor.class);    
    //持有IBasicBolt類型的變量
    private IBasicBolt _bolt;
    //定義了成員變量_collector
    private transient BasicOutputCollector _collector;

    public BasicBoltExecutor(IBasicBolt bolt) {
        _bolt = bolt;
    }
    /**
     * 實現declareOutputFields方法,但它實際上是
     * _bolt調用declareOutputFields方法
     */
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        _bolt.declareOutputFields(declarer);
    }

    /**
     * 實現prepare方法,
     * 實際上是調用_bolt的prepare方法,
     * 並實例化BasicOutputCollector
     */
    public void prepare(Map stormConf, TopologyContext context, 
            OutputCollector collector) {
        _bolt.prepare(stormConf, context);
        _collector = new BasicOutputCollector(collector);
    }
    /**
     * 實現execute方法
     */
    public void execute(Tuple input) {
        //設置運行器上下文,
        //它表示經execute方法發送出去的消息都是由輸入消息產生的,
        //即輸出的消息都將標記為輸入消息所衍生出來的消息,
        //這是使用IBasicBolt實現消息跟蹤的重要一環
        _collector.setContext(input);
        try {
            //調用_bolt的execute方法
            _bolt.execute(input, _collector);
            //對輸入的消息進行Ack操作.
            //這一步意味著基於當前輸入消息的處理和衍生消息的發送已經完成,
            //這時就可以對該消息進行Ack操作了.
            _collector.getOutputter().ack(input);
        } catch(FailedException e) {
            //Storm捕獲所有的FailedException,並對輸入的消息進行Fail操作。
            //如果捕獲的異常為ReportedFailedException的實例,
            //則調用reportError回調方法,給用戶一個機會去處理異常
            //FailedException是Storm定義的一種基本異常,用來進行消息的失敗重發等操作,
            //並不會導致Topology運行停止
            if(e instanceof ReportedFailedException) {
                _collector.reportError(e);
            }
            _collector.getOutputter().fail(input);
        }
    }
    public void cleanup() {
        _bolt.cleanup();
    }
    public Map<String, Object> getComponentConfiguration() {
        return _bolt.getComponentConfiguration();
    }
}

先說說裝飾模式的結構,裝飾模式UML類圖如圖所示:

技術分享

如上圖所示,裝飾模式的角色構成:
(1)抽象組件角色-Component:給出一個抽象接口,以規範或約束準備接收附加職責的對象.
(2)具體組件對象-ConcreteComponent:實現了組件對象的接口,通常是被裝飾器裝飾的原始對象.
(3)裝飾角色-Decorator:所有裝飾器的抽象父類,需要定義一個與組件接口一致的接口,
並持有一個Component對象,其實就是持有被裝飾的對象
(4)具體裝飾對象-ConcreteDecorator:實際的裝飾器對象,負責給組件對象添加附加的功能

現在舉一個常見的例子(沖咖啡)來說明一下。
我敲代碼困了,想沖一杯咖啡來提提神,但我怕咖啡苦,便向咖啡裏加點糖(用糖裝飾一番),喝著喝著覺得沒有牛奶香,邊往咖啡裏到點牛奶(用牛奶裝飾咖啡一番),這整個過程便可看成裝飾模式.
代碼:
1.先定義一個接口用來約束職責對象

/**
 * Component  
 * 抽象構建角色,
 * 約束或規範準備接收附加責任的對象
 */
public interface Component {
    public void operation();
}

2.實現組件對象的接口,它是被裝飾器裝飾的原始對象(這裏是咖啡)

/**
 * ConcreteComponent  
 * 接收附加責任
 */
public class ConcreteComponent implements Component {

    @Override
    public void operation() {
        System.out.println("的咖啡");
    }
}

3.定義裝飾器

/**
 * Decorator裝飾角色 
 */
public abstract class Decorator implements Component {
    //持有一個構建對象的實例
    private Component component;
    public Decorator(Component component){
        this.component=component;
    }
    @Override
    public void operation() {
        component.operation();
    }
}

4.實現加糖裝飾器和加牛奶裝飾器

/** 
 * ConcreteDecorator1
 * 加糖裝飾器
 */
public class ConcreteDecorator1 extends Decorator {

    public ConcreteDecorator1(Component component) {
        super(component);
        // TODO Auto-generated constructor stub
    }
    public void operation(){
        System.out.print("加糖");
        super.operation();
    }
}
/**
 * ConcreteDecorator2   
 * 加奶裝飾器
 */
public class ConcreteDecorator2 extends Decorator {

    public ConcreteDecorator2(Component component) {
        super(component);
        // TODO Auto-generated constructor stub
    }
    public void operation(){
        System.out.print("加奶");
        super.operation();
    }
}

5.測試

public class Test {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Component component=new ConcreteComponent();
        Decorator decorator1=new ConcreteDecorator2(component);
        Decorator decorator2=new ConcreteDecorator1(decorator1);
        decorator2.operation();
    }
}

6.測試結果

加糖加奶的咖啡

jdk中也大量使用裝飾模式。比如Java流接口中輸出流部分
OutputStream就相當於裝飾模式中的Component;
FileOutputStream、ObjectOutputStream這幾個對象直接繼承了OutputStream,
還有一些對象直接繼承OutputStream對象,比如:ByteArrayOutputStream、PipedOutputStream等.這些對象相當於裝飾模式中的ConcreteComponent,是可以被裝飾器裝飾的對象.
FilterOutputStream相當於裝飾模式中的Decorator,而他的子類DataOutputStream、BufferedOutputStream就相當於裝飾模式中的ConcreteDecorator。FilterOutputStream和它的子類對象的構造器都是傳入組件OutputStream。對照上述將的裝飾模式的結構圖,由此可見這部分類的設計完全采用裝飾模式.
同理輸入流部分也類似。

註:內容部分為學習李明老師Storm源碼分析的筆記整理。
歡迎關註下面二維碼進行技術交流:

技術分享

JStorm與Storm源碼分析(七)--BasicBoltExecutor與裝飾模式