【问题标题】:Keeping methods DRY with minor differences in each method保持方法干燥,每种方法的细微差别
【发布时间】:2017-01-16 14:50:25
【问题描述】:

我正在编写一个转换类来在提取 API 数据时使用的模型之间进行转换,做一个与实体框架一起使用的模型。两者分开的原因是由于字段上的 JSON.Net 注释,当我从 api 中提取数据和将其与 entitfy 框架和 asp.net 一起使用时,我需要的注释之间存在冲突。

我有十几个这样的类,除了一个字段外几乎相同。以下是它们的两种转换方法的示例。

public static IEnumerable<PlayerUnitsKilledRank> ConvertPlayerUnitsKilledRankings(IEnumerable<ApiCombatUnitsKilledRank> rankings, int world)
{
    List<PlayerUnitsKilledRank> dbRankings = new List<PlayerUnitsKilledRank>();
    DateTime now = DateTime.Now.Date;
    foreach (ApiCombatUnitsKilledRank rank in rankings)
    {
        PlayerUnitsKilledRank dbRank = new PlayerUnitsKilledRank()
        {
            Date = now,
            World = world,
            Player = rank.Player,
            Alliance = rank.Alliance,
            Rank = rank.Rank,
            UnitsKilled = rank.UnitsKilled
        };
        dbRankings.Add(dbRank);
    }
    return dbRankings;
}

public static IEnumerable<PlayerCavernRaidingRank> ConvertPlayerCavernRaidingRankings(IEnumerable<ApiRaidingCavernRank> rankings, int world)
{
    List<PlayerCavernRaidingRank> dbRankings = new List<PlayerCavernRaidingRank>();
    DateTime now = DateTime.Now.Date;
    foreach (ApiRaidingCavernRank rank in rankings)
    {
        PlayerCavernRaidingRank dbRank = new PlayerCavernRaidingRank()
        {
            Date = now,
            World = world,
            Player = rank.Player,
            Alliance = rank.Alliance,
            Rank = rank.Rank,
            Plundered = rank.ResourcesPlundered
        };
        dbRankings.Add(dbRank);
    }
    return dbRankings;
}

你怎样才能删除多余的代码并让我的课程保持干燥?这些方法彼此非常相似,但我想不出一个好的方法来做到这一点。

我可以使用通用方法,但我仍然需要处理那个单一的非属性。由于每个类都非常相似,我可以创建一个它们都继承自的基类,但一次性属性仍然是一个问题。

【问题讨论】:

    标签: c# dry


    【解决方案1】:

    提取ApiCombatUnitsKilledRankApiRaidingCavernRank 之间的公共接口。这个接口可以有一个方法:IRank ProduceRank()

    PlayerCavernRaidingRankPlayerUnitsKilledRank 应该继承相同的IRank 接口。

    您所指的“一次性属性”现在是具体实现问题,您实际上可以拥有任意数量的此类属性。

    public interface IRank
    {
        // Your common rank properties here
        // Maybe even create a base abstract Rank class ...
    }
    
    public interface IRankProducer
    {
        IRank ProduceRank();
    }
    
    public class PlayerCavernRaidingRank : IRank
    {
    }
    
    public class PlayerUnitsKilledRank : IRank
    {
    }
    
    public class ApiCombatUnitsKilledRank : IRankProducer
    {
        public IRank ProduceRank()
        {
            return new PlayerUnitsKilledRank()
            {
                Player = this.Player,
                Alliance = this.Alliance,
                Rank = this.Rank,
                UnitsKilled = this.UnitsKilled
            };
        }
    }
    
    public class ApiRaidingCavernRank : IRankProducer
    {
        public IRank ProduceRank()
        {
            return new PlayerCavernRaidingRank()
            {
                Player = this.Player,
                Alliance = this.Alliance,
                Rank = this.Rank,
                Plundered = this.ResourcesPlundered
            };
        }
    }
    
    public static IEnumerable<IRank> Convert(IEnumerable<IRankProducer> rankings, int world)
    {
        var dbRankings = new List<IRank>();
        DateTime now = DateTime.Now.Date;
        foreach (IRankProducer rank in rankings)
        {
            var rank = rank.ProduceRank();
            rank.World = world;
            rank.Date = now;
            dbRankings.Add(rank);
        }
    
        return dbRankings;
    }
    

    【讨论】:

      【解决方案2】:

      您还可以为ApiRankPlayerRank 创建基类,并在ApiRank 基类中公开ToPlayerRank。像ToString() 一样思考。

      abstract class PlayerRank
      {
          public DateTime Date { get; set; }
          public int World { get; set; }
          public int Player { get; set; }
          public int Alliance { get; set; }
          public int Rank { get; set;}
      }
      
      abstract class ApiRank
      {
          public int Player { get; set; }
          public int Alliance { get; set; }
          public int Rank { get; set; }
      
          // method that should be overriden in
          // concrete class that create specific player rank type
          // as well as doing type specific operation
          protected abstract PlayerRank CreatePlayerRank();
      
          // put common operation here
          public PlayerRank ToPlayerRank(int world, DateTime date)
          {
              var inst = CreatePlayerRank();
      
              inst.Player = Player;
              inst.Alliance = Alliance;
              inst.Rank = Rank;
              inst.World = world;
              inst.Date = date;
      
              return inst;
          }
      }
      
      class PlayerUnitsKilledRank : PlayerRank
      {
          public int UnitsKilled { get; set; }
      }
      
      class ApiCombatUnitsKilledRank : ApiRank
      {
          public int UnitsKilled { get; set; }
      
          protected override PlayerRank CreatePlayerRank()
          {
              var b = new PlayerUnitsKilledRank();
              b.UnitsKilled = UnitsKilled;
              return b;
          }
      }
      
      class PlayerCavernRaidingRank : PlayerRank
      {
          public int Plundered { get; set;}
      }
      
      class ApiRaidingCavernRank : ApiRank
      {
          public int Plundered { get; set;}
      
          protected override PlayerRank CreatePlayerRank()
          {
              var b = new PlayerCavernRaidingRank();
              b.Plundered = Plundered;
              return b;
          }
      }
      
      static IEnumerable<PlayerRank> ConvertRank(IEnumerable<ApiRank> rankings, int world)
      {
          DateTime now = DateTime.Now.Date;
          return rankings.Select(x=>x.ToPlayerRank(world, now));
      }
      

      【讨论】:

        【解决方案3】:

        您可以将委托传递给泛型方法以解决此问题,或者如果所有PlayerRank 都有无参数构造函数,您可以使用new() 约束。

        public static IEnumerable<TPlayerRank> ConvertRankings<TApiRank,TPlayerRank>(IEnumerable<TApiRank> rankings, int world/*, Func<TPlayerRank> func*/) 
            where TApiRank : APIRank, 
            where TPlayerRank : PlayerRank, new()
        {
            List<TPlayerRank> dbRankings = new List<TPlayerRank>();
            DateTime now = DateTime.Now.Date;
            foreach (var rank in rankings)
            {
                //TPlayerRank dbRank = func();
                var dbRank = new TPlayerRank();
        
                dbRank.Date = now,
                dbRank.World = world,
                dbRank.Player = rank.Player,
                dbRank.Alliance = rank.Alliance,
                dbRank.Rank = rank.Rank,
                dbRank.Plundered = rank.ResourcesPlundered
        
                dbRankings.Add(dbRank);
            }
            return dbRankings;
        }
        

        TApiRank 是泛型类型。您通过使用约束来指定此类型,where TApiRank : APIRank,我假设 APIRank 是类,但正如 @HristoYankov 建议的那样,您可以使用通用建议接口 IRank

        【讨论】:

          猜你喜欢
          • 2011-02-23
          • 2019-01-26
          • 2019-09-30
          • 1970-01-01
          • 1970-01-01
          • 2023-03-08
          • 2011-08-18
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多