【发布时间】:2023-03-17 03:58:01
【问题描述】:
我有一个课程ScoreStrategy,它描述了如何计算测验分数:
public class ScoreStrategy
{
public int Id { get; set; }
public int QuizId { get; set; }
[Required]
public Quiz Quiz { get; set; }
public decimal Correct { get; set; }
public decimal Incorrect { get; set; }
public decimal Unattempted { get; set; }
}
三个属性Correct、Incorrect 和Unattempted 描述了为响应分配多少分。这些点也可能是负面的。评分策略适用于测验中的所有问题,因此每个测验只能有一个ScoreStrategy。
我有两个子类:
public class DifficultyScoreStrategy : ScoreStrategy
{
public QuestionDifficulty Difficulty { get; set; }
}
public class QuestionScoreStrategy : ScoreStrategy
{
[Required]
public Question Question { get; set; }
}
我的问题有三个难度级别(Easy、Medium、Hard;QuestionDifficulty 是一个枚举)。 DifficultyScoreStrategy 指定是否需要以不同方式分配特定难度问题的分数。这将覆盖适用于整个测验的基本 ScoreStrategy。每个难度级别可以有一个实例。
第三,我有一个QuestionScoreStrategy 类,它指定是否必须以不同方式授予特定问题的分数。这将覆盖测验范围的ScoreStrategy 和难度范围的DifficultyStrategy。每个问题可以有一个实例。
在评估测验的回答时,我想实现一个逐级回退机制:
对于每个问题:
- 检查问题是否有
QuestionScoreStrategy,如果找到则返回策略。 - 如果没有,回退到
DifficultyScoreStrategy并检查是否有针对正在评估的问题的难度级别的策略 如果找到策略,则返回它。 - 如果不存在,则回退到测验范围内的
ScoreStrategy并检查是否存在,如果存在则返回, - 如果也没有
ScoreStrategy,则使用默认值{ Correct = 1, Incorrect = 0, Unattempted = 0 }(如果我也可以将其设为可配置就好了,就像.NET 的优雅方式一样:
options => {
options.UseFallbackStrategy(
correct: 1,
incorrect: 0,
unattempted: 0
);
}
)。
总结
我已将以上信息汇总在一个表格中:
| Strategy Type | Priority | Maximum instances per quiz |
|---|---|---|
QuestionScoreStrategy |
1st (highest) | As many as there are questions in the quiz |
DifficultyScoreStrategy |
2nd | 4, one for each difficulty level |
ScoreStrategy |
3rd | Only one |
| Fallback strategy (Default { Correct = 1, Incorrect = 0, Unattempted = 0}) |
4th (lowest) | Configured for the entire app. Shared by all quizzes |
我有一个名为 EvaluationStrategy 的容器类,其中包含这些评分策略以及其他评估信息:
partial class EvaluationStrategy
{
public int Id { get; set; }
public int QuizId { get; set; }
public decimal MaxScore { get; set; }
public decimal PassingScore { get; get; }
public IEnumerable<ScoreStrategy> ScoreStrategies { get; set; }
}
我尝试过的:
我在上面的同一个EvaluationStrategy 类中添加了一个名为GetStrategyByQuestion() 的方法(注意它被声明为partial),它实现了这个回退行为,并且还添加了一个伴随索引器,它反过来调用这个方法。我已经声明了两个HashSets 类型DifficultyScoreStrategy 和QuestionScoreStrategy 和一个Initialize() 方法实例化它们。然后将所有评分策略按类型切换并添加到相应的HashSet,每个测验只能有一个ScoreStrategy,将存储在defaultStrategy中:
partial class EvaluationStrategy
{
private ScoreStrategy FallbackStrategy = new() { Correct = 1, Incorrect = 0, Unattempted = 0 };
private ScoreStrategy defaultStrategy;
HashSet<DifficultyScoreStrategy> dStrategies;
HashSet<QuestionScoreStrategy> qStrategies;
public void Initialize()
{
qStrategies = new();
dStrategies = new();
// Group strategies by type
foreach (var strategy in strategies)
{
switch (strategy)
{
case QuestionScoreStrategy qs: qStrategies.Add(qs); break;
case DifficultyScoreStrategy ds: dStrategies.Add(ds); break;
case ScoreStrategy s: defaultStrategy = s; break;
}
}
}
public ScoreStrategy this[Question question] => GetStrategyByQuestion(question);
public ScoreStrategy GetStrategyByQuestion(Question question)
{
if (qStrategies is null || dStrategies is null)
{
Initialize();
}
// Check if question strategy exists
if (qStrategies.FirstOrDefault(str => str.Question.Id == question.Id) is not null and var qs)
{
return qs;
}
// Check if difficulty strategy exists
if (dStrategies.FirstOrDefault(str => str.Question.Difficulty == question.Difficulty) is not null and var ds)
{
return ds;
}
// Check if default strategy exists
if (defaultStrategy is not null)
{
return defaultStrategy;
}
// Fallback
return FallbackStrategy;
}
}
这种方法看起来有点笨拙,我觉得不太合适。使用部分类并添加到 EvalutationStrategy 似乎也不正确。如何实现这种逐级回退行为?我可以在这里使用设计模式/原则吗?如果未配置,我知道 .NET 框架中的许多事情都会退回到默认约定。我需要类似的东西。或者有人可以简单地推荐一个更清洁、更优雅、可维护性更好的解决方案吗?
注意/附加信息:所有测验的 ScoreStrategys 和 EvaluationStrategy 都存储在由 EF Core(.NET 5) 管理的数据库中,并带有 TPH 映射: em>
modelBuilder.Entity<ScoreStrategy>()
.ToTable("ScoreStrategy")
.HasDiscriminator<int>("StrategyType")
.HasValue<ScoreStrategy>(0)
.HasValue<DifficultyScoreStrategy>(1)
.HasValue<QuestionScoreStrategy>(2)
;
modelBuilder.Entity<EvaluationStrategy>().ToTable("EvaluationStrategy");
我有一个单一的基地DbSet<ScoreStrategy> ScoreStrategies 和另一个DbSet<EvaluationStrategy> EvaluationStrategies。由于EvaluationStrategy 是一个 EF Core 类,我对向其添加逻辑(GetStrategyByQuestion())也持怀疑态度。
【问题讨论】:
标签: c# .net oop design-patterns fallback