Нулевой объект дизайна шаблона проблемы с поведением
Я хочу поделиться некоторыми своими мыслями и задать несколько вопросов Шаблон проектирования пустых объектов, который должен реализовывать класс с нейтральным поведением. Итак, если мне нужно реализовать интерфейс:
public interface IProduct
{
double Cost();
string Name();
}
вероятно, я буду использовать следующую реализацию:
public class NullProduct : IProduct
{
public double Cost()
{
return 0.0;
}
public string Name()
{
return String.Empty;
}
}
Хорошо.
Но какую стратегию я должен использовать, когда мне нужно реализовать следующий интерфейс:
public interface IProduct
{
//other methods...
bool IsTasty();
}
IsTasty - нет "нейтрального" поведения. Как я должен реализовать это в классе NullProduct? Верните true или false. Нет так понятно.
Хуже, если интерфейс имеет некоторые свойства:
public interface IProduct
{
//other methods...
int Price{get;set;}
}
и реализация:
public class NullProduct : IProduct
{
//other methods...
int Price
{
get {return 0;}
set {/*nothing*/}
}
}
Зачем? Из-за, если какой-то пользователь получит объект, как здесь:
IProduct prod = GetSomeObject(); //GetSomeObject returns NullProduct
а пользователь попробуй сделать:
prod.Price = 8;
Console.WriteLine(prod.Price);
пользователь получит странный результат. Пользователь помнит, он стоит 8 долларов, но теперь цена получит 0. Инфляция?
В UnitTest такая же проблема.
Теперь я не хочу нарушать OCP, спрашивая объект, является ли он нулевым объектом.
Как вы решаете проблему?
2 ответа
Если у вас нет "нейтрального" возвращаемого результата, вы должны "придумать" его и использовать в нулевом объекте:
enum Tasty {
Yes, No, Unknown
}
public interface IProduct {
Tasty IsTasty();
}
Запись в нулевой объект почти неизбежно является ошибкой программирования, поэтому реализация Price
должно идти так:
public class NullProduct : IProduct {
//other methods...
int Price {
get {return 0;}
set { throw new InvalidOperationException(); }
}
}
Можно "полностью компоновать" шаблон проектирования NullObject в универсальную многократно используемую реализацию, поэтому вам не нужно беспокоиться об обработке всех возможных деталей в каждом случае (если для Nullify требуется тип интерфейса). Обратите внимание, что он также будет обрабатывать случай, когда ваша интерфейсная функция возвращает другой определенный пользователем тип. В этом случае он создаст другой экземпляр NullObject и вернет его, так что вы получите глубокие экземпляры NullObject последовательно.
Это универсальная многоразовая реализация NullObject.java, и здесь вы можете увидеть, как ее можно использовать TestNullObject.java