у нас тут возникла дискуссия по одному простенькому вопросу... хочу спросить мнение общественности как правильно сделать (интересует именно теоретически-идеологический аспект вопроса)
Есть класс такого вида:
public class MyClass {
private List<string> folderList;
// .... a lot of useful public methods here.....
}
все прекрасно — список фолдеров спрятан внутри, общение с внешним миром через публичные методы.
Но вот проблема. Есть GUI'ный класс Setup, через который юзер должен иметь возможность изменять список фолдеров, т.е. иметь к нему доступ.
Как идеологически правильно это можно сделать? по сути, имеет место ситуация, когда folderList должен быть приватным для всех, кроме как для класса Setup. В C++ это естесственным образом решается через friend class. а в C# ?
1. сделать folderList public ну или internal — собственно так и сделали, но как-то неправильно это по ощущениям.
2. сделать публичные методы для чтения/модификации folderList -- ну а чем это лучше чем п.1 ?
3. унаследовать Setup от MyClass не получится, т.к. Setup WPF'ный класс уже имеющий развесистый список предков,а множественное наследование C# опять же не признает. да и неправильно это
4. пока писал придумал извращенный вариант: сделать метод в классе Setup, с параметром List<string> folderList (возможно его инкапсулировать в спец.класс типа MyClassParameters); при необходимости настройки параметров вызывается нетод в классе MyClass, который дергает метод в Setup, отдавая ему ссылку на свой folderList, внутри Setup производится правка переданного списка. ура, искусственный friend сделан. Только по-моему слишком все сложно...
Здравствуйте, vitasR, Вы писали:
R>5. Ваш вариант?
Огласите основные юз-кейсы, без этого можно гадать долго. Например:
1. Пересоздание объекта MyClass после каждого изменения списка фолдеров (т.е. лист получаем в конструкторе). Минус — в ряде случаев это внесет неоправданные усложнения
2. Делаем наследника MyClassRW от MyClass, где добавляем интерфейс для модификации списка фолдеров. Порождаем MyClassRW, отдаем инстанс в GUI как MyClassRW, а всем остальным — как обычный MyClass. Минусы — потенциальные касты, да и наследование без особой нужды применять не след.
3. И т.п.
Здравствуйте, vitasR, Вы писали:
R>Hi,
R>у нас тут возникла дискуссия по одному простенькому вопросу... хочу спросить мнение общественности как правильно сделать (интересует именно теоретически-идеологический аспект вопроса)
R>Есть класс такого вида:
1) Что делает и как используется этот класс? Это что-то типа настроек приложения?
2) Верно ли предположение, что этот folderList нужно заполнять только один раз при установке программы?
Если 2 верно: передавать folderList в конструктор класса и забыть про все.
Если 2 неверно, то можно в тот же конструктор передать старый экземпляр MyClass и внутри конструктора копировать остальные поля из старого объекта в новый
Пришло в голову: сделать статический метод ChangeFolderList(MyClass claszz, List<Folder> newFolders) -- использовать ттакое неправилно заметно сложнее
По поводу первого вопроса:
если это банальные настройки приложения, то не заморачивайся и делай все сетеры публичны. Программеры у вас, надеюсь, вменяемые, так что им нах не понадобиться ломать приложение изменяя этот класс.
Мыслей еще много, но доминирует следующая: придумал человек себе проблему и решает ее, нет чтобы о чем-то действительно важном думал (без обид)
Здравствуйте, vitasR, Вы писали:
R>5. Ваш вариант?
Ввести интерфейс IMyClassFoldersEditor и реализовать его неявно в MyClass.
Хотя мне кажется, уж если MyClass должен предоставлять функции изменения списка папок, то пускай он их явно предоставляет в виде методов AddFolder, RemoveFolder и т.п., а в случае WPF более удачным решением мне видится ObservableCollection.
Здравствуйте, vitasR, Вы писали:
R>у нас тут возникла дискуссия по одному простенькому вопросу... хочу спросить мнение общественности как правильно сделать (интересует именно теоретически-идеологический аспект вопроса)
R>Есть класс такого вида:
R>public class MyClass { R> private List<string> folderList; R> // .... a lot of useful public methods here..... R>}
R>все прекрасно — список фолдеров спрятан внутри, общение с внешним миром через публичные методы.
R>Но вот проблема. Есть GUI'ный класс Setup, через который юзер должен иметь возможность изменять список фолдеров, т.е. иметь к нему доступ.
Чтобы оценить ваше решение, информации об ответственности класса недостаточно. Наиболее универсальное решение — классическое (геттеры/сеттеры, при необходимости — events для извещения других классов об изменениях). Но такое решение в некоторых случаях будет слишком тяжеловесным.
Могу предложить следующее упражнение, которое я сам использую при оценке зависимости и связанности классов, и, следовательно, для оценки качества архитектуры: попробуйте представить, что перед вами поставлена задача создать две программы с аналогичным функционалом: одна будет использовать GUI, а другая — командную строку. Как при этом изменится MyClass? А если появится еще одна задача — например, сделать аналогичный функционал, вызываемый из NAnt, или интегрировать MyClass в WEB-приложение? По идее, MyClass должен быть спроектирован так, чтобы его можно было во всех трех случаях использовать без изменений.
В любом случае, если количество классов-клиентов MyClass растет, friend уже не кажется таким удобным решением.
Здравствуйте, vitasR, Вы писали:
R>у нас тут возникла дискуссия по одному простенькому вопросу... хочу спросить мнение общественности как правильно сделать (интересует именно теоретически-идеологический аспект вопроса) R>Есть класс такого вида: [c#]
R>public class MyClass {
R> private List<string> folderList;
R> // .... a lot of useful public methods here.....
R>}
[/c#] R>все прекрасно — список фолдеров спрятан внутри, общение с внешним миром через публичные методы.
R>Но вот проблема. Есть GUI'ный класс Setup, через который юзер должен иметь возможность изменять список фолдеров, т.е. иметь к нему доступ.
R>Как идеологически правильно это можно сделать? по сути, имеет место ситуация, когда folderList должен быть приватным для всех, кроме как для класса Setup. В C++ это естесственным образом решается через friend class. а в C# ?
R>1. сделать folderList public ну или internal — собственно так и сделали, но как-то неправильно это по ощущениям.
Нет, это как раз то, что нужно. Причём не "public" (это точно "неправильно"), а именно internal.
То есть, если в С++ вы для чего-то применилибы friend в шарпе используйте internal. Заморачиваться со всякими стратегиями имеет смысл тогдла, когда вы заморочились бы с ними и в С++, то есть исключительно для дела, а не для колдунства на областью видимости.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, vitasR, Вы писали: R>Как идеологически правильно это можно сделать? по сути, имеет место ситуация, когда folderList должен быть приватным для всех, кроме как для класса Setup. В C++ это естесственным образом решается через friend class. а в C# ?
friend это неестественная мера никогда. его применяют когда другого способа в принципе не существует. такой бэкдур в языке.
а если просто головой подумать на самое лобовое решение, что мы имеем:
model — MyClass
view — польз интерфейс, отображающий MyClass
controller — та штука, к которой обращается view для изменения model
короче у вас в схеме нет контроллера и вы пытаетесь действовать в обход него — либо переложив функционал контроллера на гуй, либо поместив внутрь MyClass. поэтому и получается коряво
Здравствуйте, vitasR, Вы писали: R>public class MyClass { R> private List<string> folderList; R> // .... a lot of useful public methods here..... R>}
да, и если там действительно "lot of useful methods" то я бы выделил folderList и прочие параметры в константный класс Settings
Смоделировать friend достаточно легко, например, парой методов:
Но, как видите, такое решение хоть и работает аналогично friend в рантайме, но оно потенциально громоздко в плане поддержки.
Я бы не заморачивался высокими материями, и использовал бы модификатор internal как быстрое и грязное решение, ну, или перепроектирование этих классов в случае наличия риска повторения ситуации.
Здравствуйте, vitasR, Вы писали:
R>Hi,
R>у нас тут возникла дискуссия по одному простенькому вопросу... хочу спросить мнение общественности как правильно сделать (интересует именно теоретически-идеологический аспект вопроса)
R>Есть класс такого вида:
R>public class MyClass { R> private List<string> folderList; R> // .... a lot of useful public methods here..... R>}
R>все прекрасно — список фолдеров спрятан внутри, общение с внешним миром через публичные методы.
R>Но вот проблема. Есть GUI'ный класс Setup, через который юзер должен иметь возможность изменять список фолдеров, т.е. иметь к нему доступ.
R>Как идеологически правильно это можно сделать? по сути, имеет место ситуация, когда folderList должен быть приватным для всех, кроме как для класса Setup. В C++ это естесственным образом решается через friend class. а в C# ?
Я бы сделал так, чтоб класс Setup взаимодействовал с MyClass не непосредственно, а через абстрактный интерфейс (ISetupable). Тогда в классе MyClass можно определить вложенный закрытый класс, имеющий доступ к закрытым членам MyClass и реализующий интерфейс ISetupable:
public interface ISetupable
{
void SetupFolderList(List<string> folderList);
}
public class Setup
{
public void Perform(ISetupable setupable)
{
List<string> folderList = new List<string>();
//...
setupable.SetupFolderList(folderList);
}
}
public class MyClass
{
public void Setup(Setup setup)
{
setup.Perform(new SetupAgent(this));
}
#region Implementation
private List<string> _folderList;
private class SetupAgent : ISetupable
{
public SetupAgent(MyClass myClass) { _myClass = myClass; }
public void SetupFolderList(List<string> folderList) { _myClass._folderList = folderList; }
private MyClass _myClass;
}
#endregion
}
class Program
{
static void Main(string[] args)
{
MyClass myClass = new MyClass();
Setup setup = new Setup();
myClass.Setup(setup);
}
}
При такой архитектуре при необходимости не составит особого труда добавить транзакционность. Вообще, архитектура, в которой объекты разных классов связаны друг с другом через абстрактные интерфейсы имеет много преимуществ: большая изолированность и безопасность, лучшая расширяемость, рефакторопригодность, более простая тестируемость...