【问题标题】:How to tell a class which objects it should create? Type vs. object confusion :(如何告诉一个类它应该创建哪些对象?类型与对象混淆:(
【发布时间】:2019-04-07 08:44:03
【问题描述】:

我有一个类 (SetOfRules) 想要(以某种方式)处理规则。 任务:创建这个类的一个新实例(这很简单),然后将规则(定义在类Rules和派生类中)添加到实例中。

问题 1:我不想实例化所有规则,然后将它们作为对象传递给 SetOfRules 的 Add-method,但我想将类名称(类型)列表传递给 Add-method。我怎么做? 那么在 SetOfRules 内部实例化这样一个对象的语法是怎样的呢?

问题2:类规则和派生类不包含任何属性。它们只是转换方法的集合。 我是否必须将它们实例化为 SetOfRules 类中的一个对象,或者我可以将它们称为静态类的方法(我不能使用静态类,因为我在 Rule 类上使用继承来确保每个规则类都实现了需要)

这是我现在遇到的一些(伪)代码:

public class Rule
{
    virtual void transform( myObject ob)
    {
        ...
    }
}

public class SpecificRule1 : Rule
{
    ...
}

public class SpecificRule2 : Rule
{
    ...
}



public class SetOfRules
{
    public AddRules( ???? ListOfRuleTypes)
    {
    }
}

我想在其他地方做类似的事情:

SetOfRules easyRules = new SetOfRules();
easyRules.Add( new ??RuleTypes??[] { "SpecificRule2", "SpecificRule13", "SpecificRule22" });

感谢您的酷提示!

【问题讨论】:

  • 你能告诉我们Rule类的完整定义吗?尤其是所有公共方法。
  • 类尚未实现,因为我还不确定结构设计(如您所见)。但主要的公共函数将是方法 transform(),它将调用 myObject 类对象的一些操作。
  • 那么请告诉我们Rules是什么。他们应该检查什么?它们是否应该包含像bool Rule.Check(object) 这样的谓词方法?在不了解领域的情况下,无法真正给出设计提示。不过我可以给你一个提示——通常在设计某些东西时最好从界面开始。想想你需要你的RuleSetOfRules做什么(提供什么方法​​)并为它们创建接口IRuleISetOfRules
  • 规则不是“检查规则”,而是“转换规则”。在这种情况下,实数域很难解释,但可以这样想:myObject 是一个数学形状(正方形、三角形、圆形......),而规则(变换规则)类似于:transform(myObject ob) { 1. ob 的两倍大小 2. 在 y 轴上移动对象 10 个单位 3. 将对象旋转 10 度 }

标签: c# methods types


【解决方案1】:

示例 - 计算订单商品折扣的规则:

public class OrderItem
{
    public int ItemId { get; set; }
    public int Quantity { get; set; }
    public decimal Price { get; set; }

    public decimal Total => Quantity * Price;
}

折扣的合约(又名接口)可以这样定义:

public interface IDiscountRule
{
    decimal CalculateDiscount(OrderItem item);
}

现在让我们实现一些折扣规则

  • 基本折扣 (2%)
  • 白银折扣 (5%)
  • 黄金折扣 (10%)

类定义:

/// <summary>
/// Abstract base class for the discount rules
/// </summary>
public abstract class DiscountRule : IDiscountRule
{
    private readonly int _percent;

    protected DiscountRule(int percent)
    {
        _percent = percent;
    }

    /// <inheritdoc />
    public decimal CalculateDiscount(OrderItem item)
    {
        if (item == null)
        {
            throw new ArgumentNullException(nameof(item));
        }

        decimal discount = item.Total * (decimal) (_percent/100.0);

        return discount;
    }
}


/// <summary>
/// 2% discount
/// </summary>
public class BasicDiscountRule : DiscountRule
{
    public BasicDiscountRule() : base(2)
    {

    }
}

/// <summary>
/// 5% discount
/// </summary>
public class SilverDiscountRule : DiscountRule
{
    public SilverDiscountRule() : base(5)
    {

    }
}

/// <summary>
/// 10% discount
/// </summary>
public class GoldDiscountRule : DiscountRule
{
    public GoldDiscountRule() : base(10)
    {

    }
}

使用规则计算订单商品的折扣:

var item = new OrderItem
{
    ItemId = 1,
    Price = 50,
    Quantity = 2
};

var rule = new SilverDiscountRule();
decimal discount = rule.CalculateDiscount(item);

现在让我们创建一个复合规则,以便将多个规则(折扣)应用于一个订单项:

/// <summary>
/// To apply multiple discount rules to an order item
/// </summary>
public class CompositeDiscountRule : IDiscountRule
{
    private readonly List<Type> _discountTypes;

    public CompositeDiscountRule()
    {
        _discountTypes = new List<Type>();
    }

    public CompositeDiscountRule(List<Type> discountTypes)
    {
        if (discountTypes == null)
        {
            throw new ArgumentNullException(nameof(discountTypes));
        }

        _discountTypes = discountTypes;
    }

    public void Register<T>() where T : IDiscountRule, new()
    {
        _discountTypes.Add(typeof(T));
    }

    /// <inheritdoc />
    public decimal CalculateDiscount(OrderItem item)
    {
        decimal totalDiscount = 0;

        foreach (var discountType in _discountTypes)
        {
            IDiscountRule rule = Activator.CreateInstance(discountType) as IDiscountRule;

            if (rule != null)
            {
                totalDiscount += rule.CalculateDiscount(item);
            }
        }

        return totalDiscount;
    }
}

您可以将复合规则与类型列表一起使用:

var item = new OrderItem
{
    ItemId = 1,
    Price = 50,
    Quantity = 2
};

// composite rule with types
var compositeRule = new CompositeDiscountRule(new List<Type>
{
    typeof(GoldDiscountRule),
    typeof(BasicDiscountRule)
});

decimal discount = compositeRule.CalculateDiscount(item); // discount = 12.0

或者,作为替代方案,使用泛型:

var item = new OrderItem
{
    ItemId = 1,
    Price = 50,
    Quantity = 2
};

// composite rule using generics
compositeRule = new CompositeDiscountRule();
compositeRule.Register<BasicDiscountRule>();
compositeRule.Register<SilverDiscountRule>();

decimal discount = compositeRule.CalculateDiscount(item); // discount = 7.0

【讨论】:

    【解决方案2】:

    看起来你想要一个带有契约的单例类:

    public interface IRule
    {
        void Transform(object o);
    }
    public abstract class SingletonBase<T> where T : SingletonBase<T>, new()
    {
        public static T Instance { get; } = new T();
    }
    public class SpecificRule1 : SingletonBase<SpecificRule1>, IRule
    {
        public void Transform(object o) => throw new NotImplementedException();
    }
    public class SpecificRule2 : SingletonBase<SpecificRule2>, IRule
    {
        public void Transform(object o) => throw new NotImplementedException();
    }
    public class RuleSet : List<IRule>
    {
        public void Add<TRule>() where TRule : SingletonBase<TRule>, IRule, new()
        {
            this.Add(SingletonBase<TRule>.Instance);
        }
    }
    

    您可以通过各种方式添加规则:

    var rules = new RuleSet
    {
        SpecificRule1.Instance,
        SpecificRule2.Instance,
    };
    
    rules = new RuleSet();
    rules.Add(SpecificRule1.Instance);
    rules.Add<SpecificRule2>();
    

    【讨论】:

    • 喜欢这个主意。 3 个问题: 1. 给定 IRule x;问题:如何访问静态对象? (x.Instance 显然不起作用) 2. 给定一个实现 IRule 的类型 t。问题:如何访问静态对象? 3. 如何声明 SingletonBase x = new SpecificRule2();或者这永远不可能,我总是必须通过接口 IRule?
    • 1:无法正常访问。鉴于interface 不能公开static 成员,也不能将其转换回SingletonBase&lt;&gt;,因为具体类型未知。 但是,您可以通过反射访问它,但这意味着您不会从编译时类型检查中受益。 2:对于运行时Type,只能通过反射。因为没有“保证”这样的单例实例存在。至于泛型类型T,您可以使用RuleSet.Add&lt;TRule&gt;() where ... 方法中所示的约束来访问它。
    • 3:您始终可以new 升级任何Singleton&lt;T&gt; 的实例,因为通用约束确保这是可行。 (注意:我会改用var x = ...)。但是,您可能不应该这样做,您甚至声明自己不想实例化。您可以像这样访问static 实例:SpecificRule2.Instance
    • 要添加到#1,如果IRule x,则无需访问静态对象,因为该静态对象本身就是x(在正常情况下,如显式newing up另一个实例)。
    【解决方案3】:

    请阅读pattern Decorator。它是一种结构化设计模式,可让您通过将新行为放置在包含这些行为的包装对象中来将新行为附加到对象。

    我认为它会帮助你解决第一个问题

    有用的链接:https://refactoring.guru/design-patterns/decorator

    【讨论】:

      猜你喜欢
      • 2010-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-02
      • 1970-01-01
      • 1970-01-01
      • 2019-02-03
      • 2012-05-21
      相关资源
      最近更新 更多