(一)行为参数化
需求情景:
现在需要对某个人的Steam游戏库中的游戏进行筛选,例如:根据游戏的售价,名字,类型等作为筛选条件。下面将列举出一些解决方案,从而引出行为参数化,以及简单介绍行为参数化带来的好处。
//用例数据:游戏类型,游戏名字,游戏售价
List<Game> games = Arrays.asList(
new Game("Fps","CSGO",99.0),
new Game("MOBA","英雄联盟",0.0),
new Game("Fps","Apex",0.0),
new Game("Casual","星露谷物语",35.0)
);
解决方案一:
//在方法体中指定特定的值筛选,不灵活,对于不同的需求(获取游戏类型为MOBA类的游戏),需写大量的重复代码
public List<Game> getFpsGames(List<Game> games){
ArrayList<Game> res = new ArrayList<>();
for (Game game : games) {
if (game.getType().equals("Fps")){
res.add(game);
}
}
return res;
}
//把游戏类型作为参数进行筛选,不灵活,对于不同的需求(获取游戏售价低于100的),都需要写大量的重复代码
public List<Game> getGamesByType(List<Game> games,String gameType){
ArrayList<Game> res = new ArrayList<>();
for (Game game : games) {
if (game.getType().equals(gameType)){
res.add(game);
}
}
return res;
}
//对能想到的所有属性做筛选,多个标志位来表示通过什么属性进行筛选,当属性数目超过3个该方法不适用
public List<Game> getGamesByTypeAndName(List<Game> games,String gameType,String gameName,boolean flag){
ArrayList<Game> res = new ArrayList<>();
for (Game game : games) {
if ((flag && game.getType().equals(gameType)) || (!flag && game.getName().equals(gameName))){
//flag为true就根据游戏类型筛选,为false则根据游戏名字筛选。
res.add(game);
}
}
return res;
}
总结:这种方式虽然满足需求,但是有以下缺点:不灵活,大量重复代码,可读性较差。
//方法调用
@Test
public void test(){
List<Game> fpsGames = getFpsGames(games);
List<Game> fps = getGamesByType(games, "Fps");
//缺点:调用方法时,flag值意思不明确
List<Game> gamesByType = getGamesByTypeAndName(games, "MOBA", "星露谷物语", true);
List<Game> gamesByName = getGamesByTypeAndName(games, "MOBA", "星露谷物语", false);
}
解决方案二:
通过接口实现根据不同的属性来筛选游戏。需要以下几个步骤来完成:
- 定义一个接口(该接口只有一个抽象方法)。
- 不同的筛选条件对应一个接口实现类。
- 以多态的方式调用方法。
//接口
public interface GamePredicate {
boolean filter(Game game);
}
//实现类一
public class GameNamePredicate implements GamePredicate{
@Override
public boolean filter(Game game) {
return game.getName().equals("英雄联盟");
}
}
//实现类二
public class GameNameAndTypePredicate implements GamePredicate{
@Override
public boolean filter(Game game) {
return game.getName().equals("CSGO") && game.getType().equals("Fps");
}
}
//方法原型
public List<Game> filterGames(List<Game> games,GamePredicate p){
ArrayList<Game> res = new ArrayList<>();
for (Game game : games) {
if (p.filter(game)){
res.add(game);
}
}
return res;
}
//调用
List<Game> games1 = filterGames(games, new GameNamePredicate());
List<Game> games2 = filterGames(games, new GameNameAndTypePredicate());
List<Game> games3 = filterGames(this.games, new GamePredicate() {
@Override
public boolean filter(Game game) {
return game.getPrice() > 0.0;
}
});
总结:相较方案一,代码可读性强,更加灵活,出现新的筛选需求只需再写一个接口实现类即可。也可通过匿名类的方式省去对接口实现的繁琐。让filterGames方法接收多种行为(接口方法的不同实现)作为参数,并在内部使用,来完成不同的行为,这就是行为参数化!
缺点:代码还是有重复的。
解决方案三:Java8-Lambda表达式
//Lambda表达式详情见后面的文章
List<Game> games1 = filterGames(games, (game)->game.getName().equals("英雄联盟"));
结束语:这篇文章简单的介绍了行为参数化,以便理解Lambda表达式。