支持的操作设计模式 [英] Supported operations design pattern

查看:210
本文介绍了支持的操作设计模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个抽象类A具有枚举类型OutputType,
和抽象方法计算()的属性,需要执行certin计算并将结果输出到double [ ] **根据OutputType值。



我还有一个定义为D1,D2,D3 ... D20的类,派生自A,
其中每个Di类实现A的计算()方法不同。



问题是,并不是所有的OutputType值(calculate()输出类型)都支持每个Di



例如:



如果OutputType枚举值为Scaled,NonScaled 和ConstantLength,



一个certin Di可以支持Scaled和NonScaled,但不支持ConstantLength,另一个Di可以支持所有的操作类型,



我的问题是:



什么是正确的设计模式来实现这个那种行为?



请避免明显的如果OutputType属性接收到不支持的操作的值,请回答。






编辑:



展开问题:



有没有办法通过智能通知所有支持的操作,所以用户不必采取试错法? (除适当的文件外)

解决方案

请注意,我已经使用代码合同的先决条件完全删除了我以前的方法。



我一直在想一个应该更强大的解决方案。



为每个可能的操作类型定义一个受保护的虚拟方法的抽象类,以及 public ,不可覆盖计算方法可以调用覆盖受保护的虚拟方法?

  public abstract class Calculation 
{
public Calculation()
{
//我将所有可覆盖的方法存储在一个字典中,其中key
//是方法名称
OverridableMethods = new Dictionary< string,Func< double []>>()
{
{nameof(CalculateScaled),CalculateScaled},
{nameof(CalculateNonScaled),CalculateNonScaled},
{nameof(CalculateConstantLength),CalculateConstantLength}
};


//然后我们得到第一个覆盖方法。如果
//比一个更多,它将选择第一次发生,其他将被
//丢弃。开发人员只需覆盖一个。
MethodInfo overridenMethod = OverridableMethods.Keys
.Select(methodName => GetType()。GetMethod(methodName,BindingFlags.Instance | BindingFlags.NonPublic))
.FirstOrDefault(method => .DeclaringType!= typeof(Calculation));

Contract.Assert(overridenMethod!= null,没有提供计算操作实现);

//所以有些方法被覆盖,我存储方法名称
OverridenMethodName = overridenMethod.Name;
}

私有字符串OverridenMethodName {get; }
私人字典< string,Func< double []>>可覆盖的方法{get;

protected virtual double [] CalculateScaled()
{
throw new NotImplementedException();
}

protected virtual double [] CalculateNonScaled()
{
throw new NotImplementedException();
}

protected virtual double [] CalculateConstantLength()
{
throw new NotImplementedException();
}

//计算只访问可覆盖的方法字典
//以获取由Func< double []>封装的覆盖方法
//我立即叫它!
public double [] Calculate()=> OverridableMethods [OverridenMethodName]();


public class SomeCalculation:Calculation
{
protected override double [] CalculateScaled()
{
return new double [] { 22};
}
}

所以真的很有用!

 计算计算= new SomeCalculation(); 

//打印出22!
Console.WriteLine(calculate.Calculate()[0]);


I have an abstract class "A" that has a property of an enum type "OutputType", and an abstract method calculation() that needs to do a certin calculation and to output the result in a double[] **according to the OutputType value.

I also have a classes defined as D1,D2,D3...D20, derive from A, Where each Di class implements A's calculation() method differently.

The problem is that not all of the OutputType values (the calculation() output types) are supported in every Di

For example:

If OutputType enum values are "Scaled", "NonScaled" and "ConstantLength",

A certin Di can support "Scaled" and "NonScaled" but not support "ConstantLength" and another Di can support all of the operation types,

My question is:

What is the right design pattern to implement this kind of behaiver?

Please avoid the obvious "Throw if the OutputType property recieves a value of a non supported operation" answers.



Edit:

Expanding the question:

Is there a way to notify the user with intellisense of all the supported operation so the user wont have to take the trial and error approach? (besides a proper documentation)

解决方案

Note that I've edited my answer completely dropping my previous approach using a precondition with code contracts.

I've been thinking about a solution that should be more powerful.

What about an abstract class which defines a protected virtual method for each possible operation type, and a public, non-overridable Calculate method could call the overriden protected virtual method?

public abstract class Calculation
{
    public Calculation()
    {
        // I store all overridable methods in a dictionary where keys
        // are the method names
        OverridableMethods = new Dictionary<string, Func<double[]>>()
        {
            { nameof(CalculateScaled), CalculateScaled },
            { nameof(CalculateNonScaled), CalculateNonScaled },
            { nameof(CalculateConstantLength), CalculateConstantLength }
        };


        // Then we get the first overriden method. If there're more
        // than one, it will pick the first occurence and others will be
        // discarded. It's up to the developer to override just one.
        MethodInfo overridenMethod = OverridableMethods.Keys
                .Select(methodName => GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic))
                .FirstOrDefault(method => method.DeclaringType != typeof(Calculation));

        Contract.Assert(overridenMethod != null, "No calculation operation implementation has been provided");

        // So some method was overriden and I store the method name
        OverridenMethodName = overridenMethod.Name;
    }

    private string OverridenMethodName { get; }
    private Dictionary<string, Func<double[]>> OverridableMethods { get; }

    protected virtual double[] CalculateScaled() 
    {
        throw new NotImplementedException();
    }

    protected virtual double[] CalculateNonScaled() 
    {
        throw new NotImplementedException();
    }

    protected virtual double[] CalculateConstantLength()
    {
        throw new NotImplementedException();
    }

    // Calculate just access the overridable method dictionary
    // to get the overriden method encapsulated by a Func<double[]>
    // and I immediately call it!
    public double[] Calculate() => OverridableMethods[OverridenMethodName]();
}

public class SomeCalculation : Calculation
{
    protected override double[] CalculateScaled()
    {
        return new double[] { 22 };
    }
}

So it really works!

    Calculation calculation = new SomeCalculation();

    // Prints out 22!
    Console.WriteLine(calculation.Calculate()[0]);

这篇关于支持的操作设计模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆