开发者

C# Design Pattern - How to write code based on highly configurable user selections

开发者 https://www.devze.com 2022-12-12 18:43 出处:网络
I would like to write code without a lot of switch, if/else, and othe开发者_JS百科r typical statements that would execute logic based on user input.

I would like to write code without a lot of switch, if/else, and othe开发者_JS百科r typical statements that would execute logic based on user input.

For example, lets say I have a Car class that I want to assemble and call Car.Run(). More importantly, lets say for the tires I have a chocie of 4 different Tire classes to choose from based on the user input.

For the, i dunno, body type, letS say i have 10 body type classes to choose from to construct my car object, and so on and so on.

What is the best pattern to use when this example is magnified by 1000, with the number of configurable parameters.

Is there even a pattern for this ? Ive looked at factory and abstract factory patterns, they dont quite fit the bill for this, although it would seem like it should.


I don't think the factory pattern would be remiss here. This is how I would set it up. I don't see how you can get away from switch/if based logic as fundamentally, your user is making a choice.

public class Car {
   public Engine { get; set; }
   //more properties here
}

public class EngineFactory {
  public Engine CreateEngine(EngineType type {
     switch (type) {
        case Big:
           return new BigEngine();
        case Small:
           return new SmallEngine();
     }
  }   
}

public class Engine {

}

public class BigEngine : Engine {

}

public class SmallEngine : Engine {

}

public class CarCreator {
   public _engineFactory = new EngineFactory();
   //more factories

   public Car Create() {
    Car car = new Car();

    car.Engine = _engineFactory.CreateEngine(ddlEngineType.SelectedValue);
    //more setup to follow

    return car;
   }
}


The problem you tell of can be solved using Dependency Injection.

There're many frameworks implementing this pattern (for example, for .NET - excellent Castle.Windsor container).


I think elder_george is correct: you should look into DI containers. However, you might want to check the builder pattern (here too), which deals with "constructing" complex objects by assembling multiple pieces. If anything, this might provide you with some inspiration, and it sounds closer to your problem than the Factory.


You can get around having to use a lot of if or switch statements if you introduce the logic of registration in your factory, a registration entry would add a binding to your dictionary in your factory:

Dictionary<Type,Func<Engine>> _knownEngines;

In the above line, you bind a type to a factory function for example like so:

private void RegisterEngine<TEngineType>(Func<T> factoryFunc) where TEngineType : Engine
{
    _knownEngines.Add(typeof(TEngineType), factoryFunc);
}

This would allow you to call:

RegisterEngine<BigEngine>(() => new BigEngine()); 

on your factory

So now you have a way of allowing your factory to know about a large number of engines without needing to resort to if/switch statements. If all your engines have a parameterless constructor you could even improve the above to:

public void RegisterEngine<TEngineType>() where TEngineType : Engine, new()
{
    _knownEngines.Add(typeof(TEngineType), () => new TEngineType());
}

which would allow you to register your engines that your factory can create like so:

RegisterEngine<BigEngine>();

Now we simply need a way of associating a user input to the right type. If we have some sort of enumeration then, we might might want to map the enum values to their corresponding type. There are many ways to achieve this, either with a dictionary in a similar way as we have done already, but this time it is an enum as a key and a type as a value or by decorating the enum values with their corresponding type as demonstrated here (If you have a very large number of values, this possibility could be interesting)

But, we can skip all this and just take a shortcut and associate the enumeration with the factory function directly.

So we would make our Dictionary look like this:

Dictionary<MyEngineEnumeration,Func<Engine>> _knownEngines;

You would register your engines

public void RegisterEngine<TEngineType>(MyEngineEnumeration key) where TEngineType : Engine, new()
{
    _knownEngines.Add(key, () => new TEngineType());
}

like so: RegisterEngine(MyEngineEnumeration.BigEngine);

And then you would have some sort of create method on your factory class that takes your enumeration value as key:

public Engine ResolveEngine(MyEngineEnumeration key)
{
    // some extra safety checks can go here
    return _knownEngines[key]
}

So your code would set your

Car.Engine = EngineFactory.ResolveEngine((MyEngineEnumeration)ddlEngine.SelectedValue)

You could follow the same pattern with wheels and so on.

Depending on your requirements, following a registration/resolution approach would allow you to potentially configure your available engines externally in an xml file or a database and allow you to make more engines available without modifying the release code file but by deploying a new assembly which is an interesting prospect.

Good luck!


You could use something like this:

  1. Define a class representing an option within a set of options, ie. a TireType class, BodyType class.

  2. Create an instance of the class for each option, get the data from a store. Fill a collection, ie TireTypeCollection.

  3. Use the collection to fill any control that you show the user for him to select the options, in this way the user selects actually the option class selected.

  4. Use the obejcts selected to build the class.

If any functionality requires chnges in behavior, you could use lamdas to represent that functionality and serialize the representation of the code to save it the store; or you could use delegates, creating a method for each functionality and selecting the correct method and saving it into a delegate on object creation.

What I would consider important in this approach is that any option presented to the user is fully functional, not only a list of names or ids.


You can try the policy class technique in C++.

http://beta.boost.org/community/generic_programming.html#policy


Are you simply asking if you can create an instance of a class based on a string (or maybe even a Type object)? You can use Activator.CreateInstance for that.

Type wheelType = Type.GetType("Namespace.WheelType");
Wheel w = Activator.CreateInstance(wheelType) as Wheel;

You'd probably want to checking around the classes that you wind up creating, but that's another story.

0

精彩评论

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