(一)行为参数化

201 阅读2分钟

(一)行为参数化

需求情景:
现在需要对某个人的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);
            }

解决方案二:

通过接口实现根据不同的属性来筛选游戏。需要以下几个步骤来完成:

  1. 定义一个接口(该接口只有一个抽象方法)。
  2. 不同的筛选条件对应一个接口实现类。
  3. 以多态的方式调用方法。
//接口
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表达式。