Как по правилам ООП правильно организовать хранение состояния объекта?
Есть собственный объект - кнопка. Есть у него несколько стилей, состоящих из: Размера (int), надписи (string), цвета (System.Drawing.Color). Допустим кнопка может иметь три состояния:
(1, "A", Color.Blue),
(2, "B", Color.Red),
(3, "C", Color.Green)
Как их лучше хранить? Да так, чтоб можно было переключаться по ним вызывая, методы Next() и Prev().
Сделал так: Класс, хранящий эти поля:
internal class StyleValueClass
{
public readonly int Id;
public readonly string Name;
public readonly Color ColorValue;
public StyleValueClass(int id, string name, Color colorValue)
{
Id = id;
Name = name;
ColorValue = colorValue;
}
public StyleValueClass Next(StyleValueСontainer container)
{
//return next
}
}
Также объект, хранящий эти объекты:
internal class StyleValueСontainer
{
StyleValueClass[] styleValue;
public StyleValueСontainer(StyleValueClass[] StyleValue)
{
styleValue = StyleValue;
}
public StyleValueClass this[int index]
{
get { return StyleValue[index]; }
set { StylerValue[index] = value; }
}
}
И у кнопки уже создаю поле в котором грубо забиваю все состояния:
internal static readonly StyleValueСontainer StyleProp = new StyleValueСontainer(new[]
{
new StyleValueClass(1, "A", Color.Blue),
new StyleValueClass(2, "B", Color.Red),
new StyleValueClass(3, "C", Color.Green),
});
Чую, что я делаю ерунду, т.к. если понадобится добавить новое свойство кнопки (например shape) или добавить еще одно состояние (например new StyleValueClass(4, "D", Color.Black), то получается очень тесное связывание.
Как лучше организовать?
Ответы (1 шт):
ООП не занимается неким хранением состояний чего бы то ни было. ООП - это абстрактная парадигма, прежде всего. И для ООП нет разницы какого цвета кнопка, или что написано на этой кнопке, и так далее.
В данном случае цвет/строка на кнопке - это реализация ООП, т.е. есть абстракция кнопки, а есть то что написано на кнопке и например цвет кнопки. Но всё это НЕ касается ооп-абстракций, но касается реализаций этих ооп-абстракций. Однако же реализация этих абстракций к ООП никакого отношения не имеет.
Иначе говоря: Парадигма ООП не может различить красную и синюю кнопку. НИКАК. А значит для ООП эти кнопки одинаковые. В пределах абстракций ооп - одинаковые, разницы нет. И конечно же для различия этого существует например функциональщина, только это уже не ооп.
ООП предельно простая штука. Ищите общее - выделяйте это общее - получите ТИП. Но навыделяя много всякого разного общего - вы получите МНОГО ТИПОВ.
Абстракция это - набор абстрактных типов. Некий набор абстрактных типов объединяется под условной абстракцией. НО. "Мы" же уже выше описали то, что ООП не занимается реализацией, а значит должен быть "посредник" - между реализацией и абстракцией.
В вашем случае типы - это строки, цвета и так далее. Объединяя эти типы - вы можете получить абстракцию, и в вашем случае абстракция это и есть объединение типов цветов/строк. В вашем случае абстракция объединяет НЕ реализованные цвет/строку, и это называется абстракцией кнопки.
Далее это всё нужно как то реализовать, реализуется это через интерфейсы. Т.е. ваша кнопка будет иметь интерфейсы IColorable, IStringable и так далее. Это посредник между абстракцией и реализацией. Абстракция "торчит наружу" вовсе не конкретными типами, а абстрактными типами. Проблем нет. Это ООП.
Итого:
public interface IColorable // что то "цветообразное"
{
// Конкретные методы цвета (не реализация)
}
public interface IStringable // что то "строкообразное"
{
// Конкретные методы надписей (не реализация)
}
public class Button : IColorable, IStringable
{
// Приватные поля цвета/надписи
// Конкретные методы цвета (реализация)
// Конкретные методы надписей (реализация)
}
И понятное дело, что интерфейсы нужно объединить, в интерфейс чего то, что называется чем то "кнопкообразным":
public interface IButtonable // что то "кнопкообразное"
: IColorable, IStringable
{
// Конкретные методы кнопки (не реализация)
// Но они же есть:
// Конкретные методы цвета (не реализация)
// Конкретные методы надписей (не реализация)
}
public class Button : IButtonable
{
// Приватные поля цвета/надписи
// Конкретные методы кнопки (реализация)
// Конкретные методы цвета (реализация)
// Конкретные методы надписей (реализация)
}
И тут встаёт другая проблема, впрочем, это не проблема ООП. А как хранить в полях не конкретный тип, а любой тип? Для этого существуют типы-обобщения. Впрочем это НЕ типы - это условность языка и компилятора. Т.е. если вы не знаете какой конкретный тип цвета/строки ("мы" можем иметь собственные реализации цвета/строки), то и нужно определить конкретный тип - не как тип, а как обобщённый тип, и это уловка языка/компилятора, это НЕ ооп.
Совсем итого:
public interface IButtonable // что то "кнопкообразное"
: IColorable<TColor>, IStringable<TString>
{
// Конкретные методы кнопки (не реализация)
// Но они же есть:
// Конкретные методы цвета (не реализация)
// Конкретные методы надписей (не реализация)
}
public class Button<TColor, TString> : IButtonable
{
// Приватные поля цвета/надписи
// Работают не на конкретных типах, а на обобщённых типах
// Конкретные методы кнопки (реализация)
// Конкретные методы цвета (реализация)
// Конкретные методы надписей (реализация)
}
И так далее. Тут можно уходить в абстракции всё дальше и глубже. Например можно создать обобщённый класс-хранилище обобщённых полей. Затем от него наследовать Button. Итд итп. И накручивать абстракции на абстракции можно бесконечно. А зачем? Существует некоторый "предел" понимания этого.
Что является чем по отношению к чему?
Если вы можете ответить на этот вопрос - ваши абстракции хороши. А если не можете? Значит что-то "не то". Оно может быть криво спроектированным, или же слишком глубоким, слишком абстрактным. И то и другое - плохо. Перепроектировать можно всегда и это не проблема. Но и "упарываться в абстрактность" тоже не стоит - возникает проблема понимания всего этого - это когда уже не понятно - а что является чем по отношению к чему НА САМОМ ДЕЛЕ?
П.С. Методам Next и Previous следует быть, например, в абстракции Buttons.