Нулевой объект дизайна шаблона проблемы с поведением

Я хочу поделиться некоторыми своими мыслями и задать несколько вопросов Шаблон проектирования пустых объектов, который должен реализовывать класс с нейтральным поведением. Итак, если мне нужно реализовать интерфейс:

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

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