Language agnostic 消除相互之间具有强概念联系的类之外的耦合

Language agnostic 消除相互之间具有强概念联系的类之外的耦合,language-agnostic,Language Agnostic,我有石头,纸,和剪刀。这些是石头、布、剪刀游戏的组成部分或“手”。鉴于两名玩家的手,游戏必须决定谁赢。如何解决存储此链表的问题 不把不同的手连在一起?目标是允许在不改变任何其他手牌的情况下为游戏添加新手牌(例如Jon Skeet) 我对代理持开放态度,但对大型switch语句或代码复制不持开放态度。例如,引入一个新类型来管理链的比较是可以的,只要我不需要为我添加的每一只手更改它。再说一次,如果你能合理化拥有一个代理,它必须为每一个新的手或当一个手改变时改变,这也是受欢迎的 这是一个设计101的

我有
石头
,和
剪刀
。这些是石头、布、剪刀游戏的组成部分或“手”。鉴于两名玩家的手,游戏必须决定谁赢。如何解决存储此链表的问题

不把不同的手连在一起?目标是允许在不改变任何其他手牌的情况下为游戏添加新手牌(例如Jon Skeet)

我对代理持开放态度,但对大型switch语句或代码复制不持开放态度。例如,引入一个新类型来管理链的比较是可以的,只要我不需要为我添加的每一只手更改它。再说一次,如果你能合理化拥有一个代理,它必须为每一个新的手或当一个手改变时改变,这也是受欢迎的


这是一个设计101的问题,但我很好奇人们能想出什么解决方案。显然,这个问题可以很容易地扩展到包含更多组件的更大系统,这些组件之间具有任意复杂的关系。这就是为什么我要用一个简单而具体的例子来解决这个问题。欢迎使用任何范例,无论是OOP还是其他范例。

拥有一个实现Win方法的GameStrategy类。win方法获取一个手牌列表,如果有赢家,则返回一手牌;如果比赛是平局,则返回null。我认为获胜策略实际上不是手的财产,而是游戏的财产。在GameStrategy类中加入确定一双牌的胜利者

编辑:潜在战略

public enum RPSEnum { Rock, Paper, Scissors }

private RPSEnum FirtRPS = RPSEnum.Rock;
private RPSEnum LastRPS = RPSEnum.Scissors;

public Hand Win( Hand firstPlayer, Hand secondPlayer )
{
    if (firstPlayer.Value == FirstRPS
        && secondPlayer.Value == LastRPS)
    {
       return firstPlayer;
    }
    else if (secondPlayer.Value == FirstRPS
             && firstPlayer.Value == LastRPS)
       return secondPlayer;
    }
    else
    {
       int compare = (int)firstPlayer.Value - (int)secondPlayer.Value;
       if (compare > 0)
       {
          return firstPlayer;
       }
       else if (compare < 0)
       {
          return secondPlayer;
       }
       else
       {
          return null;
       }       
    }
}
public enum RPSEnum{石头、布、剪刀}
私人RPSEnum FirtRPS=RPSEnum.Rock;
私人RPSEnum LastRPS=RPSEnum剪刀;
公开赛获胜(第一手、第二手)
{
如果(firstPlayer.Value==FirstRPS
&&secondPlayer.Value==LastRPS)
{
返回第一名玩家;
}
else if(secondPlayer.Value==FirstRPS
&&firstPlayer.Value==LastRPS)
返回第二名球员;
}
其他的
{
int compare=(int)firstPlayer.Value-(int)secondPlayer.Value;
如果(比较>0)
{
返回第一名玩家;
}
否则如果(比较<0)
{
返回第二名球员;
}
其他的
{
返回null;
}       
}
}
要添加新的手动值,只需按正确的顺序将该值添加到RPSEnum。如果是新的“最低”手牌,则更新FirstRPS。如果是新的“最高”牌,请更新LastRPS。您根本不需要更改实际的算法


注意:这比仅三个值所需的更复杂,但要求在不更新太多代码的情况下添加附加值。

如果它们在概念上有足够的相似性,您可能不想在减少耦合的情况下将自己击倒


“耦合”实际上只是一个度量,如果某件事情的内部实现发生了更改,代码将被破坏多少。如果这些事物的内部实现对其他事物本质上是敏感的,那么它就是;减少耦合很好,但软件首先应该反映现实。

我不认为不同的手是不同的类型:它们是同一类型的单独实例。该类型具有名称、图片等属性


通过从数据中加载手名列表和一个矩阵来初始化游戏,该矩阵给出了哪只手击败了每只手。数据可能会通过比较方法加载到游戏类中。

在这种情况下,重要的是,如果比较两个对象,就会得到一个结果

因此,基本上,您需要以这样一种方式来解耦此比较,即您不仅可以将新类型的对象添加到混合中,还可以将比较规则添加到可以处理此对象的混合中

现在,我也对你的问题发表了评论,说“有时候,问题可能太笼统了”,这里的问题是,无论我告诉你如何去做你所问的事情,这对你解决真正的问题一点帮助都没有

除非你在做一个石头剪刀游戏

我个人会用我自己的IoC容器做以下事情

ServiceContainer.Global.RegisterFactory<IHandType>()
    .FromDelegate(() => RandomRockScissorPaper());
ServiceContainer.Global.RegisterFactory<IHandComparison, DefaultHandComparison>();
写了所有这些之后,我意识到我的app.config配置将无法处理委托,因此需要一个工厂对象,但同样的情况仍然适用


你需要既能解决获得新牌的方法,又能解决获胜牌的规则。

为什么要解耦?这些元素本质上都是相互联系的,添加一只新的手不会改变这一点


只要有一个基本的手类,是由岩石,布和剪刀扩展。为基类指定一个.beatenby属性,该属性接受另一个Hand类型的类。如果一只手可能被另一只手击败,只需将.beatenby属性改为数组。

其他几个答案提供了非常灵活且非常解耦的解决方案。。。而且它们比需要的复杂得多。解耦的简单答案是,很少有语言本机支持这一点。GoF访问者模式的存在是为了在支持单一分派的语言中模拟双重分派(任何OO语言都支持这一点,即使是带有函数指针的C语言)

这里有一个稍微大一点的例子。假设您正在监视HTTP流量,并且尝试根据请求方法和响应代码的组合对模式进行分类。不要看这个系列:

rock/paper
paper/scissors
paper/paper
...
GET/200
GET/304
POST/401
POST/200
...
…您将看到以下系列:

rock/paper
paper/scissors
paper/paper
...
GET/200
GET/304
POST/401
POST/200
...
如果系统具有
HttpRequest
HttpResponse
对象,最简单的分派方法是使用一个函数,该函数将路由到所有可能的选项:

HttpRequest req;
HttpResponse resp;
switch ((req.method, resp.code)) {
    case (GET, 200): return handleGET_OK(req, resp);
    case (GET, 304); return handleGET_NotModified(req, resp);
    case (POST, 404): return handlePOST_NotFound(req, resp);
    ...
    default: print "Unhandled combination"; break;
}
这样,您的底层对象就可以
GET/200
GET/304
POST/401
POST/200
...
HttpRequest req;
HttpResponse resp;
switch ((req.method, resp.code)) {
    case (GET, 200): return handleGET_OK(req, resp);
    case (GET, 304); return handleGET_NotModified(req, resp);
    case (POST, 404): return handlePOST_NotFound(req, resp);
    ...
    default: print "Unhandled combination"; break;
}