重构复杂的FlagsAttribute按位检查

时间:2017-04-25 09:36:28

标签: c# .net enums bitwise-operators

如果之前有人问过,请道歉;我甚至正在努力使用正确的术语来定义问题/目标。

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

例如,以下是我如何声明两个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]是不够的。我正处于我相当有限的OOP体验的极限,所以也许缺少一些简单的东西。

1 个答案:

答案 0 :(得分: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 { }
}