抽象方法的具体实现

时间:2016-12-22 23:59:20

标签: c# oop generics inheritance

我的目标是隐藏我正在从客户端构建的类的所有细节,包括要实例化的具体类。

客户端将枚举传递给工厂,工厂返回适当的派生类的实例,如下所示:

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>();
        }

    }

2 个答案:

答案 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());
      }
}

...让控制容器的反转为您注入所需的汽车实施!