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

Visitor模式demo類圖
在這個模式中,
Commander、Sergeat和Soldier類基本上是不能再擴展的了??梢詳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);
}
}
上面的代碼中,visitSoldier和visitSergeant方法就是典型的樣板代碼。
現(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、Sergeant和Commander類進行靈活擴展。
比如,我們需要增加一個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模式參考文獻