2012-03-15 5 views
8

Я работаю над проектом, который использует инъекцию зависимостей через Ninject. Пока это работает очень хорошо, и мне очень нравится DI, но теперь я решил, что мне нужно сериализовать некоторые объекты, и мне сложно делать это с помощью шаблонов DI.Как сериализовать объекты, созданные на фабриках

Скажем, у меня есть класс с именем Foo, который имеет список баров и делает их через завод, как это:

public class Foo 
{ 
    private List<Bar> _bars; 
    private BarFactory _barFactory; 

    ... 

    public void MakeBar() 
    { 
     _bars.Add(_barFactory.MakeBar()); 
    } 
} 

Вот Бар, который получает сделано, когда _barFactory.MakeBar() вызывается. Я хочу, чтобы бар был сериализуемым:

public class Bar : ISerializable 
{ 
    private List<IPickle> _pickles; 
    private PickleFactory _pickleFactory; 

    public Bar(PickleFactory factory) 
    { 
     _pickleFactory = factory; 
    } 

    public void MakePickle(int x) 
    { 
     _pickles.Add(_pickleFactory.MakePickle(x)); 
    } 

    public void GetObjectData(SerializationInfo info, StreamingContext context) 
    { 
     //serialize member variables here 
    } 

    //Constructor called during deserialization 
    private Bar(SerializationInfo info, StreamingContext context) 
    { 
     //fill in member variables with data from SerializationInfo 
    } 
} 

Обратите внимание, что в баре есть собственный завод и коллекция соленья. Вот проблема: когда вызывается конструктор десериализации Bar, у меня нет никакого способа получить его еще один PickleFactory. Первоначальный PickleFactory был предоставлен Bar BarFactory, но конструктор десериализации не был вызван BarFactory.

Мой текущий план решения этой проблемы состоял в том, чтобы извлечь все сериализуемые элементы Bar в свой класс BarDataObject. Тогда я бы сделал BarDataObject сериализуемым, но не сам Bar. Я бы добавил функцию BarFactory, которая принимает параметр BarDataObject в качестве параметра и создает панель для заполнения всей информацией из BarDataObject.

Однако предположим, что Pickle также имеет классы обслуживания, которые он получил от фабрики, которая изготовила его, что также не может быть сериализовано. Поэтому мне пришлось бы извлечь DataObject из Pickle, и мой BarDataObject должен был бы удерживать файл PickleDataObject. И предположим, что у Pickle была переменная-член с набором данных и сервисов? Я должен был бы создать и поддерживать DataObject для этого. Это похоже на настоящий облом, особенно учитывая, что в моем проекте есть много других вещей, которые мне нужно для сериализации, и они, вероятно, столкнутся с одной и той же проблемой.

Итак, есть ли лучшее решение? Я что-то делаю неправильно, DI-мудрый? Я только начал работать с DI и Ninject, но я не могу найти никого, кто придумал бы хороший способ сериализации объектов, которые были введены классами обслуживания.

+0

Можете ли вы добавить конструктор в свой класс 'BarFactory', который принимает информацию о сериализации? – Matthew

+3

Какой тип объекта Foo? Это «новизна» или «инъекционная»? (Http://misko.hevery.com/2008/09/30/to-new-or-not-to-new/). –

+0

@Matthew - Если бы я сделал, как бы получить SerializationInfo на заводе? К тому времени, когда был вызван частный конструктор Bar, слишком поздно попросить фабрику сделать Bar, так как мы уже находимся в конструкторе Bar, и Bar в любом случае не знает о BarFactory. – tandersen

ответ

9

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

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

Трудно точно сказать, что вы пытаетесь достичь с Bar, но от того, что я могу видеть, я бы предложил сделать Pickle быть ПОКО, и используя List<Pickle> вместо пользовательского Bar класса.

Или, если Bar предназначено иметь другую сериализуемую информацию кроме соленья, делает Bar ПОКО со свойством соленья:

public class Bar 
{ 
    public string Name {get;set;} 
    public List<Pickle> Pickles {get;set;} 
} 

Поскольку POCO которые не будет иметь зависимость, они не должны требовать заводов, поэтому этот класс должен быть полностью сериализуемым. Если есть сложные функции, которые вы хотите выполнить на Bar s и Pickle s, их следует абстрагировать в отдельные служебные службы, которые принимают Bar s и Pickle s в качестве параметров их метода.

+0

Я согласен с StripingWarrior: Вы должны разделить данные и поведение. – Steven

+0

Спасибо за ответ, похоже, что это может сработать для моей непосредственной проблемы. В принципе, вы предлагаете мне воспользоваться всеми услугами из Bar и Pickle, а Foo поговорить с этими службами (например, заводы)? Хорошо. Но что, если позже мне также понадобится сериализовать Foo? Могу ли я сделать Foo POCO и переместить все сервисы для Foos, Bars и Pickles в парня, который сделал Foo? Разве это не приведет к большому уродливому классу, у которого слишком много обязанностей? – tandersen

+1

@tandersen: Похоже, вы понимаете принцип. Это та же самая стратегия, предложенная в статье, которую Марк Симан связал в своем комментарии. (Марк буквально написал книгу о DI в .NET, кстати). Я думаю, вам будет легко отделить обязанности и уменьшить размеры классов, если вы будете следовать этому шаблону. Если нет, не стесняйтесь публиковать новый вопрос и посмотреть, может ли сообщество SO указать некоторые полезные шаблоны для вашего конкретного случая. – StriplingWarrior

Смежные вопросы