开发者

What OOP pattern to use when only adding new methods, not data?

In my app, I have deal with several different "parameters", which derive from IParameter interface, and also ParamBase abstract base class. I currently have two different parameter types, call them FooParameter and BarParameter, which both derive from ParamBas开发者_StackOverflow中文版e. Obviously, I can treat them both as IParameters when I need to deal with them generically, or detect their specific type when I need to handle their specific functionality.

My question lies in specific FooParameters. I currently have a few specific ones with their own classes which derive from FooParameter, we'll call them FP12, FP13, FP14, etc. These all have certain characteristics, which make me treat them differently in the UI. (Most have names associated with the individual bits, or ranges of bits). Note that these specific, derived FP's have no additional data associated with them, only properties (which refer to the same data in different ways) or methods.

Now, I'd like to keep all of these parameters in a Dictionary<String, IParameter> for easy generic access. The problem is, if I want to refer to a specific one with the special GUI functions, I can't write: FP12 fp12 = (FP12)paramList["FP12"] because you can't downcast to a derived type (rightfully so). But in my case, I didn't add any data, so the cast would theoretically work.

What type of programming model should I be using instead? Thanks!


There's nothing really wrong with this approach, except for maybe storing the parameters in a dictionary. What is the purpose of doing that? Especially if you key them on their class name.

I would just use a List<IParameter> and have a control go through the collection and pick the right subclass out of there.

m_Parameters = new List<IParameter>();
//This control needs FP12
foreach(var param in Parameters) {
  var fp12 = param as FP12;
  if (fp12 != null) {
    //do something with the param.
    break;
  }
}

After writing the above I think I finally understand what you are trying to do. If you want to perform an operation that is available on FP12 on any subclass of FooParameter then you need to take that operation out of FooParameter altogether. Since your parameter is data and that data is the same across different subclasses of FooParameter, it makes sense to only have one implementation of FooParameter ("data" class) and multiple "operation" classes.

//The one implementation of IParameter for all FooParameters
public class FooParameter : IParameter {
  string Data1 {get;set;}
}

//base class for Foo Operation, only stores FooParameter 
public class FooOperationBase {
  protected readonly FooParameter m_Param;
  public FooOperationBase (FooParameter param) {
    m_Param = param;
  } 
}

//specific operations on FooParameter go in this class
public class FooOperation12 : FooOperationBase {
  public FooOperation12(FooParameter param) : base(param) {}

  public void DoSomeOperation() {
    return m_Param.Data1 + " transformed";
  }
}


If paramList["FP12"] is a FP12, that cast will work. Of course, if it's not it will throw a InvalidCastException. You could also use as, if you're not sure what type the object will be.

Whether this is an ideal design is a separate issue. Ideally, you want to prefer polymorphism, meaning the subclass of FooParameter knows to use its new special functions internally, and the outside code doesn't have to cast, or use as or is.


I'm not 100% sure where you're coming from with this question, but you could do something like this:

class Program
{
    static void Main(string[] args)
    {
        var paramList = new List<IParameter>();
        paramList.Add(new FooParameter());
        paramList.Add(new BarParameter());
        paramList.Add(new F1());
        paramList.Add(new F2());

        foreach (var p in paramList)
        {
            p.DoCommonOperation();
            DoSpecificOperation(p);
        }

        Console.ReadKey();
    }

    private static void DoSpecificOperation(IParameter p)
    {
        if (p is F1)
        {
            (p as F1).F1Method();
        }
        else if (p is F2)
        {
            (p as F2).F2Method();
        }

    }

    interface IParameter 
    {
        void DoCommonOperation();
    }

    abstract class ParamBase : IParameter
    {
        public virtual void DoCommonOperation()
        {
            Console.WriteLine("ParamBase");
        }
    }

    class FooParameter : ParamBase
    {
        public override void DoCommonOperation()
        {
            Console.WriteLine("FooParameter");
        }
    }

    class BarParameter : ParamBase
    {
        public override void DoCommonOperation()
        {
            Console.WriteLine("BarParameter");
        }
    }

    class F1 : FooParameter
    {
        public override void DoCommonOperation()
        {
            Console.WriteLine("F1");
        }

        public void F1Method()
        {
            Console.WriteLine("F1.F1Method");
        }
    }

    class F2 : FooParameter
    {
        public override void DoCommonOperation()
        {
            Console.WriteLine("F2");
        }

        public void F2Method()
        {
            Console.WriteLine("F2.F2Method");
        }
    }
}

Essentially you have a method in the class that controls the list of IParameter objects that knows how to call the specific implementations, and uses is/as to do so.


Just for sanity's sake, why not use Dictionary<Type, IParameter>? With a little generics, you could do this:

public interface IParameter { }
public class FP12 : IParameter { public string fieldFP12 { get; set; } }
public class FP11 : IParameter { public string fieldFP11 { get; set; } }

public static class DictionaryHelper
{
    public static T GetParameter<T>(this Dictionary<System.Type, 
      IParameter> target) where T : IParameter
    {
        return (T)target[typeof(T)];
    }
}

Sample program and output:

class Program
{
    static void Main()
    {
        Dictionary<Type, IParameter> parameters = 
          new Dictionary<Type, IParameter>();
        parameters.Add(typeof(FP12), new FP12 { fieldFP12 = "This is FP12" });
        parameters.Add(typeof(FP11), new FP11 { fieldFP11 = "This is FP11"});

        // THIS IS WHERE YOU GET THE IPARAMETER YOU WANT - THE GENERICS WAY...
        var fp12 = parameters.GetParameter<FP12>();
        var fp11 = parameters.GetParameter<FP11>();

        Console.WriteLine(fp12.fieldFP12);
        Console.WriteLine(fp11.fieldFP11);
        Console.ReadLine();
    }
}

The resulting output:

This is FP12
This is FP11
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜