"Параметры базового класса не всегда используются" код запаха
Предположим, у вас был такой код:
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
}
}
НТН,
Космин
РЕДАКТИРОВАТЬ: Обновлен код для компиляции.