Lets say I want to make few classes to determine behaviour of agents.
The good practice would be to make some common interface for them, such interface (simplified) could look like this:interface IModel
{
开发者_如何学Python void UpdateBehaviour();
}
All , or at least most, of such model would have some parameters, but parameters from one model might have nothing in common with parameters of other model.
I would like to have some common way of loading parameters.Question
What is the best way to do that?
Is it maybe just adding method void LoadParameters(object parameters) to the IModel? Or creating empty interface IParameters and add method void LoadParameters(IParameters parameters)? That are two ideas I came up with, but I don't like either of them.It only makes sense to use a common interface when you are going to use common code to work against that interface.
In this situation, by definition, there is something in common between the classes.
Remember, interfaces are a contract, but they (conceptually) provide a different contract than inheritance. Instead of forming an object hierarchy, and defining that an object is some concrete form of another object, with interfaces, you're providing a contract that says that the object is usable in a specific way, or acts in specific manner.
In your case, an IParameterBag
or similar interface may make sense. Compare this to ISerializable
in the base class library. There, you have an interface that can be implemented by anything -but always treated in a single manner.
- you can have your parameters in a
Map
, but this is not compile-time safe. - you can have a
setParameters(..)
method which takesIParameters
(which doesn't define methods), and use downcasting in each specific implementation
An example for the 2nd option is (Java) CertPathValidator
, which takes CertPathParameters
as an input, which does not define any methods (apart from clone()
)
I met a similar problem some time ago and I was choosing between 2 solutions (code in java):
public abstract class Model {
public Model (Map<String, String> initParams) { }
}
and
public abstract class Model {
public Model (InitParams params) { }
}
where InitParams is a marker interface which has different implementations one per concrete Model subclass.
It sounds like you might want to go the route of using a marker interface and use a Factory to initialize and return an concrete instance of IModel.
1) add LoadParamsXml method to the interface
2) create a base class ModelBase
3) use LINQToXml to parse xml
4) use reflection to assign xml properties to the entity's public fields
public override bool LoadParamsXml(string source)
{
XDocument doc = XDocument.Parse(source, LoadOptions.PreserveWhitespace);
var parameters = from u in doc.Descendants("parameterType")
select new
{
id = (int)u.Attribute("id"),
name = u.Attribute("name").Value,
value = u.Attribute("value").Value,
typeId = (int)u.Attribute("typeId"),
nullValue = u.Attribute("nullValue").Value
};
//...
//..
foreach (var item in parameters)
{
SetProperty<T>(this, item.name, item.value);
}
return true;
}
protected virtual void SetProperty<T>(T obj, string propertyName, string value) where T : IModel
{
typeof(T).GetProperty(propertyName).SetValue(obj, value, null);
}
If the parameters have nothing in common, then they probably shouldn't be referenced by an interface. Here is one way you can work around this:
AgentA agentA = new AgentA();
agentA.setFooParameter("someValue");
someMethod(agentA);
AgentB agentB = new AgentB();
agentB.setBarParameter(true);
someMethod(agentB);
where someMethod accepts the interface of the agent rather than a concrete class
public void someMethod(IModel genericAgent)
{
...
}
Essentially, instantiate the agents (which all extend IModel) as concrete classes (rather than IModel agentA = new AgentA();
), set the parameters you need to on the concrete class and then pass around the interface rather than the concrete class.
精彩评论