gpt4 book ai didi

c# - 理解 SOLID 设计的单一职责原则

转载 作者:行者123 更新时间:2023-11-30 15:01:58 25 4
gpt4 key购买 nike

我目前正在尝试学习 SOLID 设计原则以及行为驱动开发,但我很难理解单一职责原则。我试图为使用测试驱动开发的 c# 找到一个很好的教程,但未能找到任何有值(value)的东西。不管怎样,在花了几天时间阅读它之后,我决定最好的学习方法是通过体验,所以我开始尽可能地使用这些原则来创建一个小应用程序。

这是一个简单的保龄球得分计算器。我认为最好的方法是从最简单的部分开始,所以我从球(或 throw )级别开始。现在我已经创建了测试和一个接口(interface)和类来处理球( throw )分数,这确保它们不是无效的,即。 <10>0 .实现后我意识到 ball 类基本上只是一个可为空的整数,所以也许我什至不需要它......但现在它就在那里。

在球之后,我决定下一个合乎逻辑的添加是框架接口(interface)和类。这就是我被卡住的地方。这是我所在的位置:

namespace BowlingCalc.Frames
{
public interface IFrame : BowlingCalc.Generic.ICheckValid
{
void AddThrow(int? score);
int? GetThrow(int throw_number);
}
}

namespace BowlingCalc.Frames
{
public class Frame : IFrame
{
private List<Balls.IBall> balls;
private Balls.IBall ball;
private int frame_number;

public Frame(Balls.IBall ball, int frame_number)
{
this.ball = ball;
this.frame_number = frame_number;
balls = new List<Balls.IBall>();
check_valid();
}

public void AddThrow(int? score)
{
var current_ball = ball;
current_ball.Score = score;
balls.Add(current_ball);
}

public int? GetThrow(int throw_number)
{
return balls[throw_number].Score;
}

public void check_valid()
{
if (frame_number < 0 || frame_number > 10)
{
throw (new Exception("InvalidFrameNumberException"));
}
}

}
}

框架使用我之前通过依赖注入(inject)实现的球类来将球得分添加到框架中。它还实现了一种返回帧中任何给定球得分的方法。

我想做的,也是我卡住的地方,是我想添加一种方法来确保帧分数有效。 (目前只是第 1-9 帧的简单情况,其中两个球的总得分必须为 10 或更少。稍后我将继续讨论更复杂的第 10 帧情况。)

问题是我不知道如何以可靠的方式实现它。我正在考虑将逻辑添加到类中,但这似乎违背了单一责任原则。然后我想将它添加为一个单独的接口(interface)/类,然后在分数更新时在每一帧上调用该类。我还想过创建一个单独的界面,IValidator或类似的东西,并在 Frame 类中实现它。不幸的是,我不知道哪一种是最好的/可靠的做事方式。

那么,实现验证帧得分的方法的好方法是什么?


注意:非常欢迎对我的代码提出任何批评。我很高兴学习创建更好的代码,并且很高兴收到任何帮助。

最佳答案

当我想到 SRP 时,我倾向于将重点放在责任方面。反过来,类的名称应该理想地描述它的责任。对于某些类,这是关于该类应该是什么的(如果框架没有行为而仅代表状态,它可能是一个很好的例子),但是当您有行为责任时,该名称就是该类应​​该是什么去“做”。

计算分数本身是一个相当小的责任,所以让我们考虑一些更大的、更自然可分解的东西。这是保龄球游戏的一个可能分解,具有简单的责任和适当的偏执封装(我们都是这里的 friend ,但没有人希望任何人错误地作弊。)

//My job is to keep score; I don't interpret the scores
public interface IScoreKeeper : IScoreReporter
{
void SetScore(int bowlerIndex, int frameIndex, int throwIndex, int score);
}

//My job is to report scores to those who want to know the score, but shouldn't be allowed to change it
public interface IScoreReporter
{
int? GetScore(int bowlerIndex, int frameIndex, int throwIndex);
}

//My job is to play the game when told that it's my turn
public interface IBowler
{
//I'm given access to the ScoreReporter at some point, so I can use that to strategize
//(more realisically, to either gloat or despair as applicable)

//Throw one ball in the lane, however I choose to do so
void Bowl(IBowlingLane lane);
}

//My job is to keep track of the pins and provide score feedback when they are knocked down
//I can be reset to allow bowling to continue
public interface IBowlingLane
{
int? GetLastScore();

void ResetLane();
}

//My job is to coordinate a game of bowling with multiple players
//I tell the Bowlers to Bowl, retrieve scores from the BowlingLane and keep
//the scores with the ScoreKeeper.
public interface IBowlingGameCoordinator
{
//In reality, I would probably have other service dependencies, like a way to send feedback to a monitor
//Basically anything that gets too complicated and can be encapsulated, I offload to some other service to deal with it
//I'm lazy, so all I want to do is tell everybody else what to do.

void PlayGame(IScoreKeeper gameScore, IEnumerable<IBowler> bowlers, IBowlingLane lane);
}

请注意,如果您想使用此模型来简单地计算分数(不玩真正的游戏),您可以有一个 stub Bowler(什么都不做)和一个 MockBowlingLane,它会生成一系列得分值。 BowlingGameCoordinator 负责当前的投球手、投球和投球,因此得分会累积。

关于c# - 理解 SOLID 设计的单一职责原则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13246977/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com