2017-04-25 55 views
0

如果之前曾詢問過,請致歉;我甚至用正確的術語來解決問題/目標。重構複雜FlagsAttribute按位檢查

Person對象...

public class Person 
{ 
    public string FullName { get; set; } 
    public HeroAbilities Abilities { get; set; } 
    public SuperHero Hero { get; set; } 
} 

...每個Person有一系列的能力,使他們潛在的超級英雄:

[FlagsAttribute()] 
public enum HeroAbilities : long 
{ 
    None = 0, 
    Strong = 1, 
    Fly = 2, 
    WarpSpeed = 4, 
    Crazy = 8 
} 

一旦我知道他們的能力是,我可以讓他們成爲一個合適的英雄:

public class SuperHero 
{ 
    public string Name { get; set; } 
} 

對於e xample,這裏是我聲明瞭兩個Person對象,檢查每一個:

// Simple check: person should travel at warp speed 
var p1 = new Person() { FullName = "Barry Allen", Abilities = HeroAbilities.Strong | HeroAbilities.WarpSpeed }; 
if(p1.Abilities.HasFlag(HeroAbilities.WarpSpeed)) 
{ 
    p1.Hero = new SuperHero() { Name = "The Flash" }; 
} 

// Complex check: person should either be crazy, or be strong AND can fly 
var p2 = new Person() { FullName = "Mr F. Bar", Abilities = HeroAbilities.None }; 
if (p2.Abilities.HasFlag(HeroAbilities.Crazy) || p2.Abilities.HasFlag(HeroAbilities.Strong) && p2.Abilities.HasFlag(HeroAbilities.Fly)) 
{ 
    p2.Hero = new SuperHero() { Name = "SuperFooB" }; 
} 

的目標是採取檢查一個Person是否適合是英雄的邏輯,並把它的每個各自SuperHero類中,所以我可以做這樣的事情:

var sh = new SuperHero() { ... }; 
var p = new Person() { .... }; 
if(sh.PersonSuitable(p)) { p.Hero = sh }; 

我真的很掙扎的部分是,一些檢查是複雜的(例如, X or (Y and Z)),所以[Enum.HasFlag][1]不足。我處於我的相當有限的面向對象的經驗的極限,所以也許錯過了一些簡單的東西。

+0

解開它的一個簡單方法是向類中添加屬性。像IsStrong,CanFly,HasWarpSpeed,IsCrazy一樣。屬性獲得者只是一行,if語句的可讀性更高。 –

回答

1

確實非常有趣的問題!恕我直言,你可以在這裏創建超級英雄檔案,這將定義一組能力和關聯到超級英雄類的一個實例。一旦被定義,你應該能夠用人的能力攔截它並檢測完美的匹配,但是記住你可能最終會有更多的一場比賽))。

聲明:該代碼可能無法按預期執行,因爲它被放在一起以僅將問題的概念性解決方案可視化。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using Microsoft.VisualStudio.TestTools.UnitTesting; 

namespace UnitTestProject4 
{ 
    [TestClass] 
    public class UnitTest2 
    { 
     [TestMethod] 
     public void TestMethod1() 
     { 
      var superHeroFactory = new SuperHeroFactory(); 

      var p1 = new Person {FullName = "Barry Allen", Abilities = {new Strong(), new WarpSpeed()}}; 
      p1.Hero = superHeroFactory.Create(p1); 

      var p2 = new Person {FullName = "Mr F. Bar", Abilities = {new Crazy(), new Strong(), new CanFly()}}; 
      p2.Hero = superHeroFactory.Create(p2); 
     } 
    } 

    public class SuperHeroFactory 
    { 
     private readonly List<Tuple<SuperHero, List<Ability>>> _profiles = 
      new List<Tuple<SuperHero, List<Ability>>> 
      { 
       new Tuple<SuperHero, List<Ability>>(
        new SuperHero {Name = "The Flash"}, new List<Ability> {new WarpSpeed()}), 
       new Tuple<SuperHero, List<Ability>>(
        new SuperHero {Name = "SuperFooB"}, new List<Ability> {new Crazy()}), 
       new Tuple<SuperHero, List<Ability>>(
        new SuperHero {Name = "SuperFooB"}, new List<Ability> {new Strong(), new CanFly()}) 
      }; 

     public SuperHero Create(Person person) 
     { 
      // you may end up with more then one match here)).. 

      return _profiles.FirstOrDefault(
       profile => !person.Abilities.Except(profile.Item2).Any())?.Item1; 
     } 
    } 

    public class Person 
    { 
     public string FullName { get; set; } 
     public ICollection<Ability> Abilities { get; } = new List<Ability>(); 
     public SuperHero Hero { get; set; } 
    } 

    public class SuperHero 
    { 
     public string Name { get; set; } 
    } 

    public abstract class Ability { } 
    public class Strong : Ability { } 
    public class CanFly : Ability { } 
    public class WarpSpeed : Ability { } 
    public class Crazy : Ability { } 
} 
+0

好的,謝謝,這很有趣(基於我的理解有限)。所以我們正在失去'enum'並用每個能力的類替換? – EvilDr

+1

正如其中一個可能的選擇,是的。如果您更喜歡枚舉,可以考慮將不同的枚舉變體分組到配置文件中,它只會更改init和query部分來檢查標記。但基本上給你更多動態的方式來管理能力的排列。 –