如何重构嵌套开关案例(Java)或何时重构(Kotlin)?

如何重构嵌套开关案例(Java)或何时重构(Kotlin)?,java,kotlin,lambda,functional-programming,stream,Java,Kotlin,Lambda,Functional Programming,Stream,要求:我需要一个Jo-Ken-p游戏的代码,可以与多个玩家一起玩。有5个角色(斯波克、剪刀、布、石头和蜥蜴)。我可以用这两个链式when/switch来实现它(下面的代码使用when(),因为它在Kotlin中,但同样的思想可以在Java中使用switch) 我读了好几个小时的书,试图找到如何使用lambda使这段代码更加优雅,但我找不到任何线索。求求你,任何帮助都会很好的 我将把整个代码粘贴到这里。尽管您看到我使用lambda至少是为了调用这个方法,但我确实缺少了lambda的一些强大功能,并

要求:我需要一个Jo-Ken-p游戏的代码,可以与多个玩家一起玩。有5个角色(斯波克、剪刀、布、石头和蜥蜴)。我可以用这两个链式when/switch来实现它(下面的代码使用when(),因为它在Kotlin中,但同样的思想可以在Java中使用switch)

我读了好几个小时的书,试图找到如何使用lambda使这段代码更加优雅,但我找不到任何线索。求求你,任何帮助都会很好的

我将把整个代码粘贴到这里。尽管您看到我使用lambda至少是为了调用这个方法,但我确实缺少了lambda的一些强大功能,并且几乎像java7中的经典方式那样进行编码

所有用户都来自H2数据库。这里是存储库

import com.mycomp.jokenpo.model.User
import org.springframework.data.repository.CrudRepository

interface UserRepository : CrudRepository<User, Long>
枚举播放类型

enum class PlayType(val value: Int) {
    SPOCK(1), TESOURA(2), LAGARTO(3), PAPEL(4), PEDRA(5)
}
服务*以下是问题*

import com.mycomp.jokenpo.enums.PlayType
import com.mycomp.jokenpo.model.User
import com.mycomp.jokenpo.respository.UserRepository
import org.springframework.stereotype.Component

@Component
class GameService(private val userRepository: UserRepository) {

    fun returnWinnerBetweenTwoPlayers(u1: User, u2: User): User {

        when (u1.play) {
            PlayType.SPOCK -> when (u2.play) {
                //SPOCK WINS
                PlayType.TESOURA -> return u1
                PlayType.PEDRA -> return u1
                //SPOCK LOSES
                PlayType.PAPEL -> return u2
                PlayType.LAGARTO -> return u2
            }
            PlayType.TESOURA -> when (u2.play) {
                //TESOURA (scissors) WINS
                PlayType.PAPEL -> return u1
                PlayType.LAGARTO -> return u1
                //TESOURA (scissors) LOSES
                PlayType.SPOCK -> return u2
                PlayType.PEDRA -> return u2
            }
            PlayType.PAPEL -> when (u2.play) {
                //PAPEL (paper) WINS
                PlayType.SPOCK -> return u1
                PlayType.PEDRA -> return u1
                //PAPEL (paper) LOSES
                PlayType.TESOURA -> return u2
                PlayType.LAGARTO -> return u2
            }
            PlayType.PEDRA -> when (u2.play) {
                //PEDRA (stone) WINS
                PlayType.LAGARTO -> return u1
                PlayType.TESOURA -> return u1
                //PEDRA (stone) LOSES
                PlayType.SPOCK -> return u2
                PlayType.PAPEL -> return u2
            }
            PlayType.LAGARTO -> when (u2.play) {
                //LAGARTO (lizard) WINS
                PlayType.SPOCK -> return u1
                PlayType.PAPEL -> return u1
                //LAGARTO (lizard) LOSES
                PlayType.TESOURA -> return u2
                PlayType.PEDRA -> return u2
            }
        }
        return u1
    }

    fun playGameWithAll(): User? {
        val allUsers = userRepository.findAll().toList()

        val winner = allUsers.reduce { a, b ->
            returnWinnerBetweenTwoPlayers(a, b)
        }

        if (allUsers.filter { player -> player.play == winner.play }
                        .count() == 1)
            return winner
        else
            return null

    }
}
上面的代码按预期工作,但我有填充我是坏的编码有两个原因:

1-我拆分了两个小lambda语句:reduce以相互比较playtype,并计算是否有多个我编码的获胜者。计数分开

2-当然,可能有一种更优雅、更易读的方法,可以用lambda编写两个链式交换机的代码,但我甚至可以尝试第一步

PS:代码是Kotlin,但是如果你用Java指向任何东西,我可以很容易地将它翻译成Kotlin。任何关于如何重构的技巧或建议都将受到高度赞赏

任何有兴趣得到游戏填充免费克隆从

***根据米哈伊尔的回答进行编辑

Main.java

    package poc;

    import java.util.List;
    import java.util.Map;
    import java.util.Optional;
    import java.util.Set;

    public class Main {

        public static void main(String[] args) {

            //Fake returned list from database
            List<User> usersList = List.of(
                    new User(1L, "Jimis", PlayType.LAGARTO),
                    new User(2L, "Drpc", PlayType.PAPEL));


            //User winnerUser = returnWinnerBetweenTwoPlayers(usersList.get(0), usersList.get(1));

            Optional<User> winnerUser  = usersList.stream().reduce( (a, b) ->
            returnWinnerBetweenTwoPlayers(a , b));

            System.out.print(winnerUser);

        }

        //Trying to refactoring from classical switch to some structure for using with lambda
        private final static Map<PlayType, Set<PlayType>> CONFIG = Map.of(
                PlayType.SPOCK,
                    Set.of(PlayType.TESOURA, PlayType.PEDRA), 
                PlayType.TESOURA, 
                    Set.of(PlayType.PAPEL, PlayType.LAGARTO)

        );

        private static User returnWinnerBetweenTwoPlayers(User u1, User u2) {
/// ****** Exception next line
            if (CONFIG.get(u1.getPlay()).contains(u2.getPlay())) {
                return u1;
            }
            return u2;
        }
    }
游戏类型枚举

package poc;

public enum PlayType {
    SPOCK,
    TESOURA, 
    PEDRA, 
    PAPEL,
    LAGARTO;
}
运行此部件CONFIG.get(u1.getPlay())时发生异常。包含(u2.getPlay())

如果我尝试在没有stream()的情况下进行simplify和call().reduce(),我也会遇到同样的问题

Exception in thread "main" java.lang.NullPointerException
    at moduleinfo/poc.Main.returnWinnerBetweenTwoPlayers(Main.java:49)
    at moduleinfo/poc.Main.main(Main.java:19)
***最终解决方案

public class Main {

    public static void main(String[] args) {

        //Fake returned list from database
        List<User> usersList = List.of(
                new User(1L, "Jogador 1", PlayType.PEDRA),
                new User(2L, "Jogador 2", PlayType.TESOURA),
                new User(3L, "Jogador 3", PlayType.TESOURA),
                new User(4L, "Jogador 4", PlayType.SPOCK)
                );


        Optional<User> winnerUser  = usersList.stream().reduce( (a, b) ->
            returnWinnerBetweenTwoPlayers(a , b));

        System.out.print(winnerUser.get().getName());

    }

    private final static Map<PlayType, Set<PlayType>> CONFIG = Map.of(
            PlayType.SPOCK,
                Set.of(PlayType.TESOURA, PlayType.PEDRA), 
            PlayType.TESOURA, 
                Set.of(PlayType.PAPEL, PlayType.LAGARTO),
            PlayType.PAPEL, 
                Set.of(PlayType.SPOCK, PlayType.PEDRA),
            PlayType.PEDRA, 
                Set.of(PlayType.LAGARTO, PlayType.TESOURA),
            PlayType.LAGARTO, 
                Set.of(PlayType.SPOCK, PlayType.PAPEL)
    );

    private static User returnWinnerBetweenTwoPlayers(User u1, User u2) {
        if (CONFIG.get(u1.getPlay()).contains(u2.getPlay())) {
            return u1;
        }
        return u2;
    }
}
公共类主{
公共静态void main(字符串[]args){
//从数据库返回的假列表
List usersList=List.of(
新用户(1L,“Jogador 1”,PlayType.PEDRA),
新用户(2L,“Jogador 2”,PlayType.TESOURA),
新用户(3L,“Jogador 3”,PlayType.TESOURA),
新用户(4L,“Jogador 4”,PlayType.SPOCK)
);
可选winnerUser=usersList.stream().reduce((a,b)->
返回两个玩家之间的Winner(a,b));
System.out.print(winneuser.get().getName());
}
私有最终静态映射配置=Map.of(
PlayType.SPOCK,
一套(PlayType.TESOURA,PlayType.PEDRA),
PlayType.TESOURA,
一套(PlayType.PAPEL,PlayType.LAGARTO),
帕佩尔,
一套(PlayType.SPOCK,PlayType.PEDRA),
PlayType.PEDRA,
一套(PlayType.LAGARTO,PlayType.TESOURA),
PlayType.LAGARTO,
一套(PlayType.SPOCK,PlayType.PAPEL)
);
两个玩家之间的私有静态用户返回Winner(用户u1、用户u2){
如果(CONFIG.get(u1.getPlay()).contains(u2.getPlay())){
返回u1;
}
返回u2;
}
}

这个
开关内部的开关
结构看起来很难阅读,你说得对

可以这样想:每个播放类型都从其他播放类型中获胜。这些信息看起来像一个配置,因此可以用声明的方式描述,如下所示:

  • 斯波克战胜特苏拉和佩德拉
  • 特苏拉战胜帕佩尔和拉加托
因此,您可以定义一个
Map
并验证Map.get(u1.play)

代码示例(java,用记事本编写,因此可能包含一些语法错误)

类游戏服务{
私有最终静态映射配置=Map.of(
PlayType.SPOCK,Set.of(PlayType.TESOURA,PlayType.PEDRA),
PlayType.TESOURA,套装(PlayType.PAPEL,PlayType.LAGARTO)
//等
);
函数User returnWinnerBetween两名玩家(用户u1、用户u2){
if(CONFIG.get(u1.getType()).contains(u2.getType()){
返回u1;
}
返回u2;
} 
}

这个问题是一个很好的例子:你正在思考/询问如何解决问题,有一个技术解决方案(lambdas)已经记在心里了。instad你应该思考/询问你想要实现的目标:可读的代码,内容和结构没有不必要的重复。这就是为什么你可能想考虑将游戏规则表示为一个好的、老式的多维数组,或者米哈伊尔建议的一个集合图,如果你更想要它ancy.@kriegaex,谢谢你的评论。我知道你在警告我如何改进我的问题。你认为可以改进什么?正如你所说的“Insetad,你应该思考/询问你试图实现的目标:可读的代码,在内容和结构上没有不必要的重复。”你是否建议我用“可读代码,内容和结构没有不必要的重复”来编辑问题标题。顺便说一句,我刚刚读了“XY问题”回答你提到的问题,我可以添加任何需要的额外信息。在我更改后,你觉得问题标题如何?一方面它更具体,另一方面也不太关注技术解决方案。不过,我没有将你的问题改写成这样。@kriegaex,谢谢。更好!我将利用你的建议改进我的下一个问题问题谢谢你的回答。我知道你有一个更好的方法,但我没有明白你的意思。请你把你的答案举例说明一下好吗?也许这对于一个对Lambda很有经验的人来说是显而易见的,但我不明白如何“定义地图并验证地图中是否包含u2.play.get(u1.play)”可以适用于我的情况。提前谢谢。谢谢。不幸的是,我不理解你的建议,或者你的目的有问题。我在上面用Java添加了结果暂定。我删除了所有不必要的额外代码,只关注你的建议,上面的版本是的,当然-你
package poc;

public enum PlayType {
    SPOCK,
    TESOURA, 
    PEDRA, 
    PAPEL,
    LAGARTO;
}
Exception in thread "main" java.lang.NullPointerException
    at moduleinfo/poc.Main.returnWinnerBetweenTwoPlayers(Main.java:39)
    at moduleinfo/poc.Main.lambda$0(Main.java:21)
    at java.base/java.util.stream.ReduceOps$2ReducingSink.accept(ReduceOps.java:123)
    at java.base/java.util.AbstractList$RandomAccessSpliterator.forEachRemaining(AbstractList.java:720)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.reduce(ReferencePipeline.java:558)
    at moduleinfo/poc.Main.main(Main.java:20)
Exception in thread "main" java.lang.NullPointerException
    at moduleinfo/poc.Main.returnWinnerBetweenTwoPlayers(Main.java:49)
    at moduleinfo/poc.Main.main(Main.java:19)
public class Main {

    public static void main(String[] args) {

        //Fake returned list from database
        List<User> usersList = List.of(
                new User(1L, "Jogador 1", PlayType.PEDRA),
                new User(2L, "Jogador 2", PlayType.TESOURA),
                new User(3L, "Jogador 3", PlayType.TESOURA),
                new User(4L, "Jogador 4", PlayType.SPOCK)
                );


        Optional<User> winnerUser  = usersList.stream().reduce( (a, b) ->
            returnWinnerBetweenTwoPlayers(a , b));

        System.out.print(winnerUser.get().getName());

    }

    private final static Map<PlayType, Set<PlayType>> CONFIG = Map.of(
            PlayType.SPOCK,
                Set.of(PlayType.TESOURA, PlayType.PEDRA), 
            PlayType.TESOURA, 
                Set.of(PlayType.PAPEL, PlayType.LAGARTO),
            PlayType.PAPEL, 
                Set.of(PlayType.SPOCK, PlayType.PEDRA),
            PlayType.PEDRA, 
                Set.of(PlayType.LAGARTO, PlayType.TESOURA),
            PlayType.LAGARTO, 
                Set.of(PlayType.SPOCK, PlayType.PAPEL)
    );

    private static User returnWinnerBetweenTwoPlayers(User u1, User u2) {
        if (CONFIG.get(u1.getPlay()).contains(u2.getPlay())) {
            return u1;
        }
        return u2;
    }
}