我的目标是隐藏我正在从客户端构建的类的所有细节,包括要实例化的具体类。
客户端将枚举传递给工厂,工厂返回适当的派生类的实例,如下所示:
IAutomobile auto = Autofactory.GetAuto(param);
我创建了一个完成大部分工作的基类,但是我需要特定的派生类来实现特定的逻辑。
public interface IAutomobile
{
void Rename(string p1, int p2);
}
public abstract class Automobile : IAutomobile
{
// Lots of properties and methods...
public abstract void Rename(string p1, int p2);
}
public class Jeep : Automobile
{
public override void Rename(string p1, int p2)
{
// Uses p1 & p2 with the base class
}
}
现在客户可以致电:
auto.Rename("s1", "s2");
一切都很好,直到我创建另一个使用不同签名实现“Rename()”方法的派生类。
public class Audi : Automobile
{
public override void Rename(int i, SomeOtherClass someOtherClass)
{
//Does something with params
}
}
第二个实现具有不同的签名,这不会令人惊讶地抛出编译错误。
所以,我尝试通过使用这样的字典使参数传递更多“灵活”:
public interface IAutomobile
{
void Rename(Dictionary<object, object> parameters);
}
客户端:
public class client
{
public void Client()
{
var parameters = new Dictionary<object, object>();
IAutomobile auto = AutoFactory.GetFactory();
parameters.Add("Param1Name", "string1");
parameters.Add("Param2Name", 21);
auto.Rename(parameters);
}
}
派生类:
public class Audi : Automobile, IAutomobile
{
public override void Rename(Dictionary<object, object> parameters)
{
// cast/box/unbox from dictionary objects...
// does something unique for Audis...
}
}
所以这就是我用字典实现它的方式,其中参数是键/值对,但这似乎糟糕。我不知道它是怎么给我买的。
或者,我可以创建表示所有派生类参数的并集的对象,但同样,这看起来很糟糕。
有人可以提出一种方法,我可以安排这个,以便不需要施法,拳击和取消拳击吗?
我不介意公开要与Rename()方法一起使用的对象,如:
auto.Rename(AudiFeatures);
或
auto.Rename(JeepFeatures);
(顺便说一下,我真的不想传递个别参数,我想传递物体)
这样的客户端调用不起作用,因为我必须在客户端代码中硬编码类型。我想在工厂中对其进行硬编码,但接下来我将如何返回正确的接口类型,以及工厂的返回类型是什么?
我不想这样做:
IAutomobile<JeepRenamingData> auto = Autofactory.GetAuto<JeepRenamingData>(param);
我想这样做:
IAutomobile auto = Autofactory.GetAuto(param);
auto.Rename(myJeepRenamingData);
让工厂返回正确的类型:
public static IAuto GetAutomobileManager(AutoMaker manufacturer)
{
switch (manufacturer)
{
case AutoMaker.Jeep
return new Jeep<JeepNamingOptions>();
case AutoMaker.Audi
return new Audi<AudiNamingOptions>();
}
}
答案 0 :(得分:1)
直接的想法是使用泛型。你能做到吗?
public interface IAutomobile { }
public interface IAutomobile<P1, P2> : IAutomobile
{
void Rename(P1 p1, P2 p2);
}
public abstract class Automobile<P1, P2> : IAutomobile<P1, P2>
{
// Lots of properties and methods...
public abstract void Rename(P1 p1, P2 p2);
}
public class Jeep : Automobile<string, int>
{
public override void Rename(string p1, int p2)
{
// Uses p1 & p2 with the base class
}
}
客户端调用将如下所示:
IAutomobile<string, int> auto = Autofactory.GetAuto<string, int>(param);
auto.Rename("Foo", 42);
IAutomobile auto_plain = auto;
根据您的更新,这将是我的建议:
IAutomobile<JeepFeatures> auto = Autofactory.GetAuto<JeepFeatures>(param);
auto.Rename(new JeepFeatures());
IAutomobile auto_plain = auto;
public interface IAutomobile { }
public interface IAutomobile<P> : IAutomobile
{
void Rename(P parameters);
}
public abstract class Automobile<P> : IAutomobile<P>
{
public abstract void Rename(P parameters);
}
public class Jeep : Automobile<JeepFeatures>
{
public override void Rename(JeepFeatures parameters)
{
}
}
答案 1 :(得分:0)
对我来说,无论哪种方式,您目前的方法都存在设计缺陷。
虽然你想要完全隐藏混凝土汽车的细节,但你需要具体的细节,因为你不能使用你完全不知道的汽车...总之,我看到过多的抽象强>
您不应该想要像IAutomobile
那样过于抽象的界面来操纵您的汽车,而应该使用界面的强大功能从头开始解决您的问题:
public interface ICanRename<TArgs>
where TArgs : class
{
void Rename(TArgs args);
}
public interface IAutomobile
{
// Other common behaviors
}
public interface IJeep : IAutomobile, ICanRename<JeepRenameArgs>
{
}
public class JeepRenameArgs {}
public class Jeep: IJeep
{
public void Rename(JeepRenameArgs args)
{
// Rename stuff
}
}
public class AutomobileFactory
{
private Dictionary<Type, Type> AutomobileImplementations = new Dictionary<Type, Type>
{
{ typeof(IJeep), typeof(Jeep) }
};
public TAutomobile Create<TAutomobile>()
where TAutomobile : IAutomobile
{
TAutomobile automobile = (TAutomobile)Activator.CreateInstance(AutomobileImplementations[typeof(TAutomobile)]);
// Some initialization stuff here
return automobile;
}
}
所以你可以按如下方式获得汽车实施:
AutomobileFactory automobileFactory = new AutomobileFactory();
IJeep jeep = automobileFactory.Create<IJeep>();
jeep.Rename(new JeepRenameArgs());
// or even:
ICanRename<JeepRenameArgs> jeep2 = automobileFactory.Create<IJeep>() as ICanRename<JeepRenameArgs>;
jeep2.Rename(new JeepRenameArgs());
即使不了解实现或其界面,看看如何重命名Jeep
!
看起来你最终会重新发明轮子。上面的代码甚至你的代码似乎是控件容器服务定位器的基本反转。
使用像Castle Windsor这样的东西呢?
WindsorContainer container = new WindsorContainer();
container.Register
(
Classes.FromThisAssembly().BasedOn<IAutomobile>()
.WithServiceAllInterfaces().LifestyleTransient()
);
IJeep jeep = container.Resolve<IJeep>();
或者,为了不邪恶(你应该避免service locator anti-pattern),你应该利用dependency injection:
public class YourClass
{
public YourClass(IJeep jeep)
{
Jeep = jeep;
}
private IJeep Jeep { get; }
public void DoStuff()
{
Jeep.Rename(new JeepArgs());
}
}
...让控制容器的反转为您注入所需的汽车实施!