如何在实现类似于reddits帖子排序的功能时避免切换案例

问题描述 投票:1回答:2

我有所有排序策略的接口ISortPostsStrategy(SortPostsByTop,SortPostsByBest,SortPostsByNew)我需要使用选项进行排序,为所有排序类型选择时间帧而没有一个我尝试使用策略模式,但问题是ISortPostsStrategy具有SortPostByNew不需要的时间帧参数和它最终被废弃了。

public interface ISortPostsStrategy
{
    Task<IEnumerable<Post>> SortAsync(string userId, DateTime startDate);
}


public class SortPostsByNew : ISortPostsStrategy
{
    private readonly UnitOfWork unitOfWork;

    public SortPostsByNew(UnitOfWork unitOfWork)
    {
        this.unitOfWork = unitOfWork;
    }

    public async Task<IEnumerable<Post>> SortAsync(string userId, DateTime startDate)
    {
        var dbPosts = await this.unitOfWork.Posts.GetBySubcribedUserOrderedByNewAsync(userId);
        return dbPosts;
    }
}

public class SortPostsByBest : ISortPostsStrategy
{
    private readonly UnitOfWork unitOfWork;

    public SortPostsByBest(UnitOfWork unitOfWork)
    {
        this.unitOfWork = unitOfWork;
    }

    public async Task<IEnumerable<Post>> SortAsync(string userId, DateTime startDate)
    {
        var dbPosts = await this.unitOfWork.Posts.GetBySubscribedUserOrderedByBestAsync(userId, startDate);
        return dbPosts;
    }
}

这是我在战略模式之前试图避免的转换案例

IEnumerable<Post> dbPosts = null;

        if (sortType == PostSortType.New)
        {
            dbPosts = await this.redditCloneUnitOfWork.Posts
                .GetBySubcribedUserOrderedByNewAsync(dbUser.Id);
        }
        else if (sortType == PostSortType.Top)
        {
            dbPosts = await this.redditCloneUnitOfWork.Posts
                .GetBySubcribedUserOrderedByTopAsync(dbUser.Id, startDate);
        }
        else if (sortType == PostSortType.Controversial)
        {
            dbPosts = await this.redditCloneUnitOfWork.Posts
                .GetBySubscribedUserOrderedByControversialAsync(dbUser.Id, startDate);
        }
        else if (sortType == PostSortType.Best)
        {
            dbPosts = await this.redditCloneUnitOfWork.Posts
                   .GetBySubscribedUserOrderedByBestAsync(dbUser.Id, startDate);
        }

        var models = this.mapper.Map<IEnumerable<PostConciseViewModel>>(dbPosts);
c# design-patterns switch-statement
2个回答
0
投票

这里的根本问题是用户界面必须将用户的需求转换为机器友好的格式 - 在这种情况下,是您的一种排序策略。因此,某些代码必须查看用户的输入,并可能使用if子句决定采用哪种逻辑路径以及实例化哪种策略。用户界面编程的这个方面是不可避免的。

放置此类代码的最安全,最合理的地方是尽可能靠近用户界面控件。有几个原因:

  1. 通过将转换代码放在用户界面模块本身中,可以避免其他代码形成依赖关系。
  2. 用户界面设计往往比业务规则更快地变化,因为用户找到了解决同一问题的无穷无尽的新方法。当事情同时发生变化时,将它们放在源代码中可以简化代码和构建管理,QA和部署问题。
  3. 代码的丑陋只在用户界面设计的上下文中才有意义,因此将它们组合在一起可以使未来的开发人员更容易理解正在发生的事情。

至于设计模式,这是我让开发人员编写看起来有点意义的代码的少数领域之一。要求的实际情况是,设计要针对人为因素进行优化,而不是机器因素,而不是建筑优雅。换句话说,使用switch/case是“好的”。

话虽如此,将switch/case置于不同的数据检索调用周围是有问题的,因为通常围绕这些调用存在逻辑,而您不会重复这些调用。所以策略模式并不坏。我会建议一个可以返回策略的工厂模式,给定一系列用户输入,可能在DTO中传递。

在较高的水平,它可能看起来像这样:

var dto = new GetSortedPostsRequestDto
{
    StartDate = inputForm.StartDatePicker.SelectedDate,
    SortMode = inputForm.GetSelectedSortMode()
};
var strategy = GetSortStrategy(dto);
var results = unitOfWork.GetSortedPosts(strategy);

当DTO来自您的c#代码之外时,这种方法尤其有用,例如:如果它是由Javascript放在一起并通过REST调用发送。

GetSortStrategy()方法是你的case/switch存在的地方。虽然工厂类不一定是不可能的,但它通常可以写成非通用的工厂方法。

ISortStrategy GetSortStrategy(GetSortedPostsRequestDto dto)
{
    switch case dto.SortMode
    {
        case SortMode.New: return new SortByNewStrategy();
        case SortMode.Top: return new SortByTopStrategy();
        case SortMode.Recent: return new SortByRecentStrategy(dto.StartDate);
        //etc.
    }
}

请注意,此模式允许您传递startDate,当且仅当它适用于用户的选择时。


0
投票

根据我的理解,问题是DateTime是一个(或一些)具体策略的实现细节。因此,高级接口不应该具有此信息。但是,如果您仍需要跨多个策略使用此DateTime,则可以创建BaseTimeDependentPostSortingStrategy(或您应该选择的任何名称)。

您正在使用dependency injection将DateTime提供给方法。您可以在构造函数中注入需要时间跨度的类型。

public interface ISortPostsStrategy
{
    Task<IEnumerable<Post>> SortAsync(string userId);
}

public abstract class BaseTimeDependentPostSortingStrategy : ISortPostsStrategy
{
    private readonly DateTime _startDate;

    protected BaseTimeDependentPostSortingStrategy(DateTime startDate)
    {
        _startDate = startDate;
    }

    public abstract Task<IEnumerable<Post>> SortAsync(string userId);
}

public class SortPostsByNew : ISortPostsStrategy
{
    private readonly UnitOfWork unitOfWork;

    public SortPostsByNew(UnitOfWork unitOfWork)
    {
        this.unitOfWork = unitOfWork;
    }

    public async Task<IEnumerable<Post>> SortAsync(string userId)
    {
        var dbPosts = await this.unitOfWork.Posts.GetBySubcribedUserOrderedByNewAsync(userId);
        return dbPosts;
    }
}

public class SortPostsByBest : BaseTimeDependentPostSortingStrategy 
{
    private readonly UnitOfWork unitOfWork;

    public SortPostsByBest(UnitOfWork unitOfWork, DateTime startDate) : base(startDate)
    {
        this.unitOfWork = unitOfWork;
    }

    public async Task<IEnumerable<Post>> SortAsync(string userId)
    {
        var dbPosts = await this.unitOfWork.Posts.GetBySubscribedUserOrderedByBestAsync(userId, _startDate);
        return dbPosts;
    }
}

免责声明:这可能无法编译,我还没有测试过。

如果这不能回答您的问题,请提供其他信息以便我提供帮助。

© www.soinside.com 2019 - 2024. All rights reserved.