Новый класс С# только с одним свойством: производный от базы или инкапсулировать в новый?

Я попытался быть описательным :) Это скорее проблема стиля программирования, чем проблема кодирования сама по себе.

Предположим, у нас есть:

A:

public class MyDict {
     public Dictionary<int,string> dict;
     // do custom-serialization of "dict"
     public void SaveToFile(...);
     // customized deserialization of "dict"
     public void LoadFromFile(...);
} 

B:

public class MyDict : Dictionary<int,string>
{

}

Какой вариант будет лучше с точки зрения стиля программирования? класс B: должен быть десериализован извне.

Основная проблема: лучше ли создать новый класс (который будет иметь только одно свойство - например, opt A:) или создать новый производный класс - например, opt B:? Я не хочу никакой другой обработки данных, кроме добавления/удаления и десериализации в поток.

Заранее спасибо!


person Gobol    schedule 19.03.2010    source источник


Ответы (7)


Зачем вообще нужно создавать новый класс? Или, скорее, почему это должен быть класс, который может загружать и сохранять самого себя? Почему бы не иметь методы в другом месте:

public void SaveDictionary(Dictionary<int, string> dictionary, string fie)

public Dictionary<int, string> LoadDictionary(string file)
person Jon Skeet    schedule 19.03.2010
comment
+1 вы могли бы обобщить и использовать SaveDictionary<TKey,TValue>( IDictionary<TKey,TValue> dictionary, string file ), и у вас была бы реализация, которая будет работать для многих типов словарей. - person tvanfosson; 19.03.2010
comment
Хех, ты прав. Я полагаю, я слишком далеко зашел в заповеди инкапсуляции ООП, и я попытался инкапсулировать все в классы... - person Gobol; 19.03.2010
comment
Я думаю, что оба варианта (ООП против процедурного) здесь вполне допустимы. Все сводится к тому, как этот класс будет использоваться, и какой выбор вам более удобен. - person FallenAvatar; 19.03.2010
comment
@ TJMonk15 - я не уверен, что назвал бы это процедурным решением. Я думаю, что @Jon просто говорит, что было бы лучше выделить сериализацию в отдельный класс. В конце концов, почему Словарь должен знать или заботиться о том, как он сохраняется. Похоже, это не функция словаря, а скорее сериализатор. - person tvanfosson; 19.03.2010
comment
@TJMonk15: Я согласен с тванфоссоном. Что, если позже он захочет сохранить Dictionary в другом формате? Объектно-ориентированный не означает втиснуть все в один класс. Если вы это сделаете, где инкапсуляция или разделение задач? - person kyoryu; 19.03.2010
comment
@Luiscencio: Единственное известное нам пользовательское поведение — это сериализация, которая IMO взаимодействует с состоянием словаря, а не является частью самого состояния. Вот почему я не вижу никакой выгоды в том, чтобы соединить их вместе. - person Jon Skeet; 19.03.2010

Я бы выбрал вариант B. Я бы использовал вариант A только в том случае, если я хотел использовать словарь за кулисами (т. е. сделать его частным/защищенным) и предоставить только ограниченное количество функций из словаря в моем новом классе.

Если вы предлагаете всю функциональность словаря, а затем и некоторые, очевидным решением будет наследование от словаря (а-ля вариант Б)

person FallenAvatar    schedule 19.03.2010

Из ответа, который я написал на другой вопрос:

Когда вы хотите «скопировать»/открыть API базового класса, вы используете наследование. Если вы хотите только «скопировать» функциональность, используйте делегирование.

Один из примеров: вы хотите создать стек из списка. В стеке есть только pop, push и peek. Вы не должны использовать наследование, учитывая, что вам не нужны функции push_back, push_front, removeAt и др. в стеке.

С другой стороны, ни один из подходов не кажется наиболее «подходящим» для вашей дилеммы. Возможно, вы могли бы создать вспомогательный класс, который выполняет всю работу по сериализации, например:

class DictionarySerializationHelper
{
    public static void Serialize(Dictionary<int, String> d, File f){
    //...
    }
    public static Dictionary<int, String> Deserialize(File f)
    {
    //...
    }
}

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

person Anzurio    schedule 19.03.2010
comment
Первоначально я также сделал свои примеры методов статическими, но могут быть случаи, когда вам нужны методы экземпляра - вам может даже понадобиться интерфейс IDictionaryStorage, чтобы он мог взаимозаменяемо сериализоваться в/из разных форматов. Все зависит от конкретных требований. - person Jon Skeet; 19.03.2010

Мои 2 цента:

В примере A я бы использовал этот стиль, когда хочу скрыть тот факт, что мой код использует Dictionary от потребителя моего класса.

В примере B — я бы сделал это, если бы не возражал против того, чтобы потребитель знал, что я использую словарь.

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

Хотелось бы услышать комментарии по этому поводу.

ХТН.

person Sunny    schedule 19.03.2010

Лучшее решение вашей проблемы зависит от варианта использования. Вы не говорите нам, как класс используется в вашем проекте. Если вы хотите иметь вполне нормальный словарь и просто хотите добавить методы для загрузки и сохранения, вам следует взглянуть на методы расширения:

  public static class DictionaryExtensions
  {
        public static void Save(this Dictionary<int,string> yourDict, ...) {...}
        public static void Load(this Dictionary<int,string> yourdict, ...) {...}
  }

Теперь вы можете сохранить экземпляр следующим образом:

Dictionary<int,string> dict = ....;
dict.Save(...);
person Achim    schedule 19.03.2010

Вы всегда можете использовать пару методов расширения

public static class DictionaryExtensions
{
    public static void Save<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, string fileName)
    {
        // TODO: save logic
    }

    public static void Load<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, string fileName)
    {
        // TODO: load logic
    }
}

Или, если вы будете использовать только Dictionary<int, string>

public static class DictionaryExtensions
{
    public static void Save(this Dictionary<int, string> dictionary, string fileName)
    {
        // TODO: save logic
    }

    public static void Load(this Dictionary<int, string> dictionary, string fileName)
    {
        // TODO: load logic
    }
}
person fre0n    schedule 19.03.2010
comment
Создание методов расширения для универсального IDictionary‹K, V› обеспечивает наибольшую гибкость. Все, что реализует тот же интерфейс, может быть сериализовано и десериализовано с помощью ваших новых методов расширения. - person Frode N. Rosand; 19.03.2010
comment
@Frode: методы расширения не дают вам возможности указать формат хранения в другом месте; если вы укажете интерфейс хранилища с помощью методов загрузки/сохранения, вы можете реализовать это в зависимости от требований, а затем внедрить эту реализацию интерфейса хранилища во все, что в ней нуждается. - person Jon Skeet; 19.03.2010

Вы можете создать свой собственный класс, который реализует как IDictionary<TKey, TValue>, так и IXmlSerializable (или в зависимости от того, какой интерфейс сериализации требует желаемой функциональности, вам, возможно, придется создать свой собственный). Кажется, я где-то читал, что по какой-то причине было предпочтительнее реализовать интерфейс IDictionary<TKey, TValue>, чем наследовать от Dictionary<TKey, TValue>.

Редактировать: C# наследуется от Dictionary, перебирает KeyValuePairs — это вопрос, который я задал о наследовании от класса Dictionary. Смотрите ответы там, почему это плохая идея.

person Sarah Vessels    schedule 19.03.2010