Абстрактный класс, как избежать дублирования кода? - PullRequest
5 голосов
/ 16 февраля 2012

У меня есть следующий код

internal abstract class Base
{
    public DateTime Time;
    public string Message;
    public string Log;
    public abstract void Invoke(string message);
}

internal class SubA : Base
{
    public override void Invoke(string message)
    {
        Time = DateTime.Now;
        // Do A
    }
}

internal class SubB : Base
{
    public override void Invoke(string message)
    {
        Time = DateTime.Now;
        // Do B
    }
}

У меня есть эти классы SubA и SubB, которые наследуются от базового класса, вы можете видеть, что у меня есть код, который сам повторяется, который устанавливает времяспособ перенести настройку времени в базовый класс?

Ответы [ 6 ]

5 голосов
/ 16 февраля 2012

Вы можете сделать что-то вроде этого:

internal abstract class Base
{
    public DateTime Time;
    public string Message;
    public string Log;
    public void Invoke(string message){
         Time = DateTime.Now;
         this.InvokeInternal(message);
    }
    protected abstract void InvokeInternal(string message);
}

internal class SubA : Base
{
    protected override void InvokeInternal(string message)
    {
        // Do A
    }
}

internal class SubB : Base
{
    protected override void InvokeInternal(string message)
    {
        // Do B
    }
}
3 голосов
/ 16 февраля 2012

Существует множество возможных решений.

Это зависит от того, когда вы хотите установить это свойство.

Если вы хотите немедленно, вы можете сделать это в конструкторе вашего Base класса.

internal abstract class Base
{
    public DateTime Time;
    public string Message;
    public string Log;
    public abstract void Invoke(string message);

    public Base()
    {
        Time = DateTime.Now;
    }
}

internal class SubA : Base
{
    public override void Invoke(string message)
    {
        // Do A
    }
}

internal class SubB : Base
{
    public override void Invoke(string message)
    {
        // Do B
    }
}
2 голосов
/ 16 февраля 2012
internal abstract class Base
{
    public DateTime Time;
    public string Message;
    public string Log;
    public virtual void Invoke(string message)
    {
        Time = DateTime.Now;
    }

}

internal class SubA : Base
{
    public override void Invoke(string message)
    { 
        base.Invoke(message);
        // Do A
    }
}

internal class SubB : Base
{
    public override void Invoke(string message)
    {
        base.Invoke(message);
        // Do B
    }
}
2 голосов
/ 16 февраля 2012

Вместо этого используйте виртуальный метод:

internal abstract class Base
{
    public DateTime Time;
    public string Message;
    public string Log;
    public virtual void Invoke(string message) {
        Time = DateTime.Now;
    }
}

internal class SubA : Base
{
}

internal class SubB : Base
{
}

Вы все еще можете переопределить метод в подклассах, где вы хотите другую реализацию.

0 голосов
/ 16 февраля 2012

Есть два практических варианта, в зависимости от того, насколько строгими должны быть ваши кодовые контракты.

Вы можете переместить логику в виртуальный метод и позволить дочерним типам перегружать поведение, если они того пожелают.

internal abstract class Base
{
    ...
    public virtual void Invoke(string message)
    {
        Time = DateTime.Now;
    }
}

internal class SubA : Base
{
    public override void Invoke(string message)
    {
        base.Invoke(message);
        // Do A
    }
}

internal class SubB : Base
{
    public override void Invoke(string message)
    {
        base.Invoke(message);
        // Do B
    }
}

Однако это позволяет производным типам вообще не вызывать базовый метод.

Если это катастрофически, если базовая функциональность не вызывается, и вы хотите больше уверенности в ожидаемом поведении, вы можете заключить более сильный контракт, предоставив точку внедрения в середину базового метода:

internal abstract class Base
{
    ...
    public void Invoke(string message)
    {
        Time = DateTime.Now;
        this.InvokeCore(message);
    }

    protected abstract void InvokeCore(string message);
}

internal class SubA : Base
{
    public override void Invoke(string message)
    {
        // Do A
    }
}

internal class SubB : Base
{
    public override void InvokeCore(string message)
    {
        // Do B
    }
}
0 голосов
/ 16 февраля 2012

Ответов уже много. В качестве альтернативного (и немного ниндзя) подхода я предложу Lambda выражения, используемые со свойствами метода.

В вашем случае;

 public class Base
  {
    public DateTime Time;
    public string Message;
    public string Log;
    public Action<string> Invoke { get; set; }

    public Base()
    {
       this.Invoke = InvokeDefault;
    }

    private void InvokeDefault(string message)
    {
       Time = DateTime.Now;
    }
  }

Таким образом, мы предоставляем поведение по умолчанию для класса base. С помощью лямбда выражений вы можете создавать экземпляры с различными Invoke методами следующим образом.

var myInstance= new Base
  {
    Invoke = () => { Time = DateTime.Now.AddDays(7); }
  };

Метод invoke переопределяется только для этого экземпляра класса Base. Это дает больше гибкости и помогает избежать ненужных подклассов.

Проверьте этот потрясающий пост от Патрика Стила для подробностей.

...