Должен ли объект записывать себя в файл или другой объект должен воздействовать на него для выполнения операций ввода-вывода? - PullRequest
47 голосов
/ 23 мая 2009

ПРИМЕЧАНИЕ: извините за длинный вопрос!

Я пытаюсь понять некоторые ключевые области, лежащие в основе ориентации объекта, и я не мог так или иначе решить свой конкретный вопрос.

Допустим, у меня есть объект, полный прекрасных данных. Класс боб.

Bob myBob = new Bob("This string is data");

Допустим, я хочу сохранить содержимое myBob в файл xml (bob.xml)

Должен ли я иметь объектное действие на Бобе для записи содержимого или мне нужно, чтобы myBob делал это?

Случай 1: действовать на объект

Writer myWriter = new Writer(myBob, "C:\\bob.xml");

Случай 2: метод сохранения

myBob.Save("C:\\bob.xml");

Некоторые люди придерживаются первого варианта, поскольку это означает, что если код для записи файлов изменяется, его не нужно обновлять при каждом методе Save; продвижение повторного использования кода, я полагаю. Моя проблема с этим - получить все данные из объектов, которые могут иметь личные данные без доступа.

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

Или ответ на мой вопрос - одна из тех «зависимых от случая» проблем? Если так, как вы узнаете, когда один метод предпочтительнее другого?

Ответы [ 7 ]

36 голосов
/ 23 мая 2009

Правильный подход, как правило, к вашему случаю 1. Это сохраняет единственную ответственность за класс (независимо от того, что он делает), не связывая его с определенным механизмом сохранения (диск).

Вы смотрите на конкретный случай более обобщенной проблемы: сериализации. Хорошо и хорошо, что у объекта есть некоторые средства для указания того, как он должен быть сериализован - это единственный объект, который знает, что необходимо для его десериализации, в конце концов. Но если вы заставляете объект сохранять себя на диск, вы тесно связали этот объект с конкретной реализацией.

Вместо этого рассмотрите возможность создания интерфейса, который обобщенный «писатель» может использовать для «сериализации» объекта во все, что этот писатель сериализует. Таким образом, вы сможете сериализовать на диск, в сеть, в память, на все, что вам нужно для сериализации. :)

25 голосов
/ 23 мая 2009

Я бы заставил Bob знать, как сериализовать себя, поскольку у него есть личные данные. Другой объект (например, Writer) возьмет его и поместит на диск. Боб знает, как лучше всего обращаться со своими данными, но ему не нужно заботиться о том, как и где они хранятся. Ваш Writer знает, как лучше всего сохранять данные, но ему не нужно заботиться о том, как эти данные создаются.

9 голосов
/ 23 мая 2009

Это пример использования шаблона разработки стратегии . Ваш myBob объект может иметь экземпляр класса, который его запишет. Возможно, вы захотите, чтобы писатель реализовал интерфейс или наследовал абстрактный класс, чтобы можно было легко изменить процедуру сохранения. Сегодня вы сохраняете файл в формате xml, но, возможно, в конечном итоге вам также потребуется сохранить объект в базе данных. Этот шаблон позволит вам легко изменить процедуру сохранения. Вы даже можете изменить способ сохранения во время выполнения.

3 голосов
/ 23 мая 2009

Еще один метод - использовать шаблон посетителя. Пусть ваш объект содержит метод Accept, который проходит через элементы, которые вы хотите обработать / сериализовать, и ваш посетитель будет вашим сериализатором. Всякий раз, когда вы обновляете или изменяете свою сериализацию (простой текст с xml на бинарный на любой другой), вам не нужно обновлять объект.

У нас был хороший опыт работы на этом пути. Это довольно мощный.

3 голосов
/ 23 мая 2009

Раньше я предпочитал вариант 2; однако, поскольку я действительно начал понимать и моделировать домены, над которыми я работаю, я предпочитаю вариант 1.

Представь, если ты моделируешь Транспорт. Почему транспортное средство знает, как сохранить себя? Он может знать, как двигаться, как начать и как остановиться, но что такое «Сохранить» в контексте транспортного средства.

0 голосов
/ 23 мая 2009

Сделайте это:

public interface Writable {
    public void Save(Writer w);
}

public interface Writer {
    public void WriteTag(String tag, String cdata);
}

public class Bob : Writable {
    private String ssn = "123-23-1234";
    public void Save(Writer w) {
        w.WriteTag("ssn", ssn);
    }
}

public class XmlWriter : Writer {
    public XmlWriter(Sting filename) {...}
    public void WriteTag(String tag, Sting cdata) {...}
}

Очевидно, что это не полное решение, но вы должны получить общее представление.

0 голосов
/ 23 мая 2009

Я думаю, что правильный подход - это вариант 1, но ваш класс может быть определен таким образом, чтобы использовать преимущества обоих подходов:

class Bob {

    IWriter _myWriter = null;

    public Bob(){
        // This instance could be injected or you may use a factory
        // Note the initialization logic is here and not in Save method
        _myWriter = new Writer("c://bob.xml")
    }

    //...
    public void Save(){

        _myWriter.Write(this);    

    }
    // Or...
    public void Save(string where){

        _myWriter.Write(this, where);

    }
    //...
}

Это можно легко изменить, чтобы поместить логику записи и инициализацию в базовый класс, чтобы класс Боба был даже чище и не зависел от постоянства.

...