"Параметры базового класса не всегда используются" код запаха

Предположим, у вас был такой код:

public Base
{
   abstract void Register();
}

public Registrator1: Base
{
   override void Register()
   {
      //uses the current state of the object to populate the UI captions
   }
}

public Registrator2: Base
{
   override void Register()
   {
      //uses the current state of the object to populate the UI captions
   }
}

Но когда вы получаете новое бизнес-правило с просьбой написать Registrator3, который на самом деле регистрируется на основе какого-либо параметра, и вы меняете кодовую базу следующим образом:

public Base
{
   abstract void Register(externalParam);
}

public Registrator1: Base
{
   override void Register(externalParam)
   {
      //uses the current state of the object to populate theUI
   }
}

public Registrator2: Base
{
   override void Register(externalParam)
   {
      //uses the current state of the object to populate the UI
   }
}

public Registrator3: Base
{
   override void Register(externalParam)
   {
     //uses a DDD - service passed in the params to populate the UI
   }
}

Но Registrator1 и Registrator2 этот параметр не нужен, и код становится вонючим. Как можно переписать этот код?

3 ответа

Решение

Вы можете использовать объект в качестве параметра здесь; который обычно используется в сценариях, где количество параметров может варьироваться в зависимости от используемого вызова.

struct RegistrationInfo
{
    public static readonly RegistrationInfo Empty = new RegistrationInfo();
    public string Username;
    public string CustomerName;
    public string Validity; 
}

abstract class Base
{
    public abstract void Register(RegistrationInfo info);
    // If you want to retain the paramaterless call:
    public void Register()
    {
         Register(RegistrationInfo.Empty);
    }
}

class Registrar1 : Base
{
    public override void Register(RegistrationInfo info)
    {
        if (info.Username == null) throw new ArgumentNullException("info.Username");
    }
}

class Registrar2 : Base
{
    public override void Register(RegistrationInfo info)
    {
        if (info.CustomerName == null) throw new ArgumentNullException("info.CustomerName");
    }
}

Преимущество этого заключается в том, что вам не нужно изменять параметры метода (что нарушает интерфейс) каждый раз, когда добавляется параметр. Использование также становится несколько самодокументированным:

var r = new Registrar1();
r.Register(new RegistrationInfo(){ Username = "JimJoe" });
r.Register(RegistrationInfo.Empty);

Это как освежитель воздуха для такого типа запаха кода, хотя он все еще вонючий; Вы можете сделать это пахнет лучше.

Наконец, вы можете сделать сайт-уборщик более чистым, сделав его params аргумент (это имеет небольшое количество накладных расходов); Честно говоря, хотя это более вонючий, потому что это языковой взлом. Наконец, вы можете улучшить его с помощью дженериков:

class RegistrationInfo
{

}

class RegistrationInfo1 : RegistrationInfo
{
    public string Arg;
}

class RegistrationInfo2 : RegistrationInfo
{
    public int Arg;
}

interface IBase<in TRegistration>
    where TRegistration : RegistrationInfo
{
    void Register(TRegistration registration);
}

class Base : IBase<RegistrationInfo>
{
    public void Register(RegistrationInfo registration)
    {

    }
}

class Registrar1 : IBase<RegistrationInfo1>
{
    public void Register(RegistrationInfo1 arg)
    {
    }
}

class Registrar2 : IBase<RegistrationInfo2>
{
    public void Register(RegistrationInfo2 arg)
    {
    }
}

Разве нельзя содержать логику для externalParam в Registrator3? Другими словами, Registrator3 использует параметр, а затем вызывает неизмененную базу без параметров?

Многое действительно зависит от того, где логика принадлежит. Если это что-то присущее базе, то поместите это в базу и либо перегрузите функцию Register(), либо предоставьте значение по умолчанию для параметра, чтобы подклассы не нуждались в его предоставлении.

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

public class Base
{
   public virtual void Register(object externalParam)
   {
       // base registration logic goes here
   }
}

public class Registrator1: Base
{
   public override void Register(object externalParam)
   {
       base.Register(null);
       // custom registration logic goes here
   }
}

public class Registrator2: Base
{
   public override void Register(object externalParam)
   {
       base.Register(null);
       // custom registration logic goes here
   }
}

public class Registrator3: Base
{
   public override void Register(object externalParam)
   {
       base.Register(externalParam);
       // custom registration logic goes here
   }
}

НТН,

Космин

РЕДАКТИРОВАТЬ: Обновлен код для компиляции.

Другие вопросы по тегам