保持方法DRY在每种方法中存在细微差别

时间:2017-01-16 05:35:22

标签: c# dry

我正在编写一个转换类,用于在提取API数据时使用的模型之间进行转换,执行与实体框架一起使用的模型。两者分开的原因是由于字段上的JSON.Net注释,从api中提取数据时以及在使用权限框架和数据时使用它之间存在冲突。 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;
}

你怎么能删除多余的代码并保持我的课干?这些方法彼此非常相似,但我无法想到一个很好的方法来做到这一点。

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

3 个答案:

答案 0 :(得分:3)

提取ApiCombatUnitsKilledRankApiRaidingCavernRank之间的公共接口。此接口可以使用单个方法:IRank ProduceRank()

PlayerCavernRaidingRankPlayerUnitsKilledRank应该继承相同的IRank界面。

一次性物业&#39;你所指的现在是一个具体的实现问题,你实际上可以拥有任意数量的这类属性。

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;
}

答案 1 :(得分:1)

您还可以为ApiRankPlayerRank创建基类,并在ToPlayerRank基类中公开ApiRank。像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));
}

答案 2 :(得分:0)

您可以将委托传递给泛型方法以解决此问题,或者如果所有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