函數(shù)式編程下的visitor模式

深入理解函數(shù)式編程之monad中,我們詳細講述了monad模式,以及monad模式和functor模式之間的區(qū)別。
這次,我們來使用monad到常規(guī)的設計模式中。
我們選取Visitor模式來作為第一個monad的應用。
Visitor模式在Visitor模式示例有一個典型的例子,其類圖如下所示:

Visitor模式demo類圖
Visitor模式demo類圖

在這個模式中,CommanderSergeatSoldier類基本上是不能再擴展的了??梢詳U展的是UnitVisitor的子類。
從這個類圖中,我們也可以看到,要實現(xiàn)一個示例的Visitor模式,需要8個類,并且有大量的樣板代碼:

public class CommanderVisitor implements UnitVisitor {

private static final Logger LOGGER = LoggerFactory.getLogger(CommanderVisitor.class);

@Override
public void visitSoldier(Soldier soldier) {

}

@Override
public void visitSergeant(Sergeant sergeant) {

}

@Override
public void visitCommander(Commander commander) {
    
    LOGGER.info("Good to see you {}", commander);
    
}

}

上面的代碼中,visitSoldiervisitSergeant方法就是典型的樣板代碼。
現(xiàn)在,我們使用monad來實現(xiàn)Visitor模式,又會是什么樣子呢?
首先是Soldier類:

public class Soldier {
    @Override
    public String toString() {
        return "soldier";
    }
}

其次是Sergeat類:

public class Sergeant {

    private List<Soldier> soldiers = new ArrayList<>();

    public void addSoldier(Soldier soldier)
    {
        this.soldiers.add(soldier);
    }

    public List<Soldier> getSoldiers() {
        return Collections.unmodifiableList(soldiers);
    }

    @Override
    public String toString() {
        return "sergeant";
    }
}

然后是Commander類:

public class Commander {

    private List<Sergeant> sergeants = new ArrayList<>();

    public void addSergeant(Sergeant sergeant)
    {
        this.sergeants.add(sergeant);
    }

    public List<Sergeant> getSergeants() {
        return Collections.unmodifiableList(sergeants);
    }

    @Override
    public String toString() {
        return "commander";
    }
}

最后是monad類:

public class VistorMonad<T> {

    private List<T> ts;

    public VistorMonad(List<T> ts) {
        this.ts = ts;
    }

    public<U> VistorMonad<U> flatMap(Function<? super T, List<U>> mapper) {
        return new VistorMonad<U>(this.ts.stream().flatMap(t -> mapper.apply(t).stream()).collect(Collectors.toList()));
    }
}

可以看到,在monad類的flatMap方法中,我們明細有代碼做了拆箱功能:

this.ts.stream().flatMap(t -> mapper.apply(t).stream())

functor中是不會有這樣的拆箱工作的,因為functor里面的包裝是一層包裝,而monad里面的包裝是多層包裝,這是他們之間的最大區(qū)別。
最后,我們來使用這個模式.
先是初始化數(shù)據(jù):

    Sergeant sergeant1 = new Sergeant();
    sergeant1.addSoldier(new Soldier());
    sergeant1.addSoldier(new Soldier());
    sergeant1.addSoldier(new Soldier());

    Sergeant sergeant2 = new Sergeant();
    sergeant2.addSoldier(new Soldier());
    sergeant2.addSoldier(new Soldier());
    sergeant2.addSoldier(new Soldier());

    Commander commander = new Commander();
    commander.addSergeant(sergeant1);
    commander.addSergeant(sergeant2);

    List<Commander> commanders = new ArrayList<>();
    commanders.add(commander);

最后就可以使用Visitor模式了:

new VistorMonad<Commander>(commanders).flatMap(c -> {
        LOGGER.info("Good to see you {}", commander);
        return commander.getSergeants();
    }).flatMap(s -> {
        LOGGER.info("Hello {}", s);
        return s.getSoldiers();
    })
    .flatMap(soldier -> {
        LOGGER.info("Greetings {}", soldier);
        return new ArrayList<>();
    });

是不是很簡單,省了三個Visitor類。
除了代碼簡單了很多以外,還可以對Soldier、SergeantCommander類進行靈活擴展。
比如,我們需要增加一個General類,就很容易了:

public class General {

    private List<Commander> commanders = new ArrayList<>();

    public void addCommander(Commander commander)
    {
        this.commanders.add(commander);
    }

    public List<Commander> getCommanders() {
        return Collections.unmodifiableList(commanders);
    }

    @Override
    public String toString() {
        return "general";
    }
}

我們再對這四個類做Visitor模式的測試!
首先,還是初始化數(shù)據(jù):

    Sergeant sergeant1 = new Sergeant();
    sergeant1.addSoldier(new Soldier());
    sergeant1.addSoldier(new Soldier());
    sergeant1.addSoldier(new Soldier());

    Sergeant sergeant2 = new Sergeant();
    sergeant2.addSoldier(new Soldier());
    sergeant2.addSoldier(new Soldier());
    sergeant2.addSoldier(new Soldier());

    Commander commander = new Commander();
    commander.addSergeant(sergeant1);
    commander.addSergeant(sergeant2);

    General general = new General();
    general.addCommander(commander);

    List<General> generals = new ArrayList<>();
    generals.add(general);

測試:

new VistorMonad<General>(generals).flatMap(g -> {
        LOGGER.info("Hi {}", g);
        return g.getCommanders();
    })
            .flatMap(c -> {
                LOGGER.info("Good to see you {}", c);
                return c.getSergeants();
            })
            .flatMap(s -> {
        LOGGER.info("Hello {}", s);
        return s.getSoldiers();
    })
            .flatMap(soldier -> {
                LOGGER.info("Greetings {}", soldier);
                return new ArrayList<>();
            });

看看,是不是擴展很簡單?


參考文獻:Visitor模式參考文獻

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容