Здравствуйте, nikov, Вы писали:
N>Дружественные (friend) классы — полезная ли это концепция? И почему в .NET от нее отказались?
Она только запутывает архитектуру приложения, делает большей частных связей между классами, усложняя её понимание. С другой стороны, без дружественных классов можно обойтись, как и без множественного наследования.
Вообще, говорят, использование дружественных классов является признаком плохой архитектуры приложения
Здравствуйте, FDSC, Вы писали:
FDS>Она только запутывает архитектуру приложения, делает большей частных связей между классами, усложняя её понимание. С другой стороны, без дружественных классов можно обойтись, как и без множественного наследования.
Точно. И вообще без наследования, кстати, можно обойтись. Отличные есть языки программирования без всего этого — С, например.
(не, я понимаю, что наследование всё-таки полезнее дружбы, просто, имхо, подобные аргументы критики не выдерживают)
FDS>Вообще, говорят, использование дружественных классов является признаком плохой архитектуры приложения
Врут. Не верьте им.
Признаком плохой архитектуры является использование дружественных классов неправильно. То же можно сказать про множественное наследование, обычное наследование, функции, макросы и т.п.
Честно говоря, сам очень редко использую. На дружественных классах/функциях делаются фабрики классов, делаются операторы-не-члены-коласса.
Хотя, есть еще "методическая" сторона. Если в 99% процентах случаев на практике возможность используется именно неправильно, возможно, стоит выкинуть её из языка. Но тогда я начал бы всё же с goto
В C# они эмулируются при помощи модификатора internal и вынесения классов в отдельную сборку. А вообще, исключение дружественных классов — это дурь проектировщиков шарпа, ИМХО.
Здравствуйте, AndreiF, Вы писали:
AF>Здравствуйте, SergH, Вы писали:
AF>В C# они эмулируются при помощи модификатора internal и вынесения классов в отдельную сборку. А вообще, исключение дружественных классов — это дурь проектировщиков шарпа, ИМХО.
Ну, я бы сказал, у меня, когда я начинал изучать C# ощущение было очень простое: "всё, что есть в Delphi — есть в C#, остальное решили не делать"
Здравствуйте, Igor Trofimov, Вы писали:
FDS>>Ну, я бы сказал, у меня, когда я начинал изучать C# ощущение было очень простое: "всё, что есть в Delphi — есть в C#, остальное решили не делать"
iT>Ну, во-первых не все, а во-вторых, не только это
Ну, я же говорю про впечатление. Конечно не только. Мне это впечатление очень мешало первое время: различий-то там много
N>Дружественные (friend) классы — полезная ли это концепция? И почему в .NET от нее отказались?
С. Макконнелл, Совершенный код. 2-е издание
Избегайте использования дружественных классов
Иногда — например, при реализации шаблона Состояние (State) — дисциплинированное использование дружественных классов помогает управлять сложностью (Gamma et al., 1995).
Однако обычно дружественные классы нарушают инкапсуляцию. Они увеличивают объем кода, о котором приходится думать в каждый конкретный момент времени, повышая тем самым сложность программы.
Полностью солидарен с автором книги в этом вопросе.
Здравствуйте, sharcUs, Вы писали:
U>С. Макконнелл, Совершенный код. 2-е издание
U>
U>Избегайте использования дружественных классов
U>Иногда — например, при реализации шаблона Состояние (State) — дисциплинированное использование дружественных классов помогает управлять сложностью (Gamma et al., 1995).
U>Однако обычно дружественные классы нарушают инкапсуляцию. Они увеличивают объем кода, о котором приходится думать в каждый конкретный момент времени, повышая тем самым сложность программы.
U>Полностью солидарен с автором книги в этом вопросе.
Если не использовать френдов, то вместо того чтобы показать внутренности одному классу, придется так или иначе показать их всем. В результате объем кода, "о котором приходится думать в каждый конкретный момент времени" возрастает на порядки. Т.о. френды как раз таки способствуют инкапсуляции.
Здравствуйте, Zuka, Вы писали:
Z>Здравствуйте, sharcUs, Вы писали:
Z>Если не использовать френдов, то вместо того чтобы показать внутренности одному классу, придется так или иначе показать их всем. В результате объем кода, "о котором приходится думать в каждый конкретный момент времени" возрастает на порядки. Т.о. френды как раз таки способствуют инкапсуляции.
В БОЛЬШИНСТВЕ случаев необходимость использования друзей свидетельствует о неправильном архитектурном решении, приводящем к усложнению объектной модели проекта и его дальнейшем сопровождении. К примеру, у меня НИ РАЗУ не появилась потребность в их использовании, хотя в прошлом уже не один завершенный проект, как на С++ так и на С#. Как правило в 98% процентах случаев включение и наследование вполне достаточно для решения требуемых задач.
Вместе с тем я не отрицаю, что в исключительных случаях использование друзей является ЕДИНСТВЕННО ВОЗМОЖНЫМ решением, однако ДЕЙСТВИТЕЛЬНАЯ необходимость их использования ограничивается единичными случаями, как и говорит Стив в приведенной мной цитате выше. Как правило потребность в дружественных сущностях является ОШИБКОЙ в архитектуре программной модели, от которой следует избавляться и использовать вместо нее другие подходы.
Здравствуйте, sharcUs, Вы писали:
U>В БОЛЬШИНСТВЕ случаев необходимость использования друзей свидетельствует о неправильном архитектурном решении, приводящем к усложнению объектной модели проекта и его дальнейшем сопровождении. К примеру, у меня НИ РАЗУ не появилась потребность в их использовании, хотя в прошлом уже не один завершенный проект, как на С++ так и на С#. Как правило в 98% процентах случаев включение и наследование вполне достаточно для решения требуемых задач.
Есть ещё другой вариант — использовать интерфейсы, выдаваемые "по требованию". Скажем, есть параметр, и всем только читать его могут и лишь "избранные" писать, делаем отдельный интерфейс "сеттерный" и функцию получения его. Связность получается более прослеживаемая, и добавляется большая расширяемость (т.к. для новых "сеттеров" не надо будет править исходный класс, добавляя в него френдов)
Здравствуйте, nikov, Вы писали:
N>Дружественные (friend) классы — полезная ли это концепция? И почему в .NET от нее отказались?
На мой взгляд — полезная. А в дотнете очень могого нет по сугубо личностным соображениям его создателей и по причинам неудобности реализации (ну, типа это потребует много сил, так что ну его на).
Другое дело, что вообще, на мой взгляд проблемы инкапсуляции несколько преувеличены. Да и большинство проблем с инкапсуляцией появляются вовсе не из-за ограничений языка/среды, а в следствии невнимательности разработчиков.
ЗЫ
Кстати, умнее было бы сделать не дружественные классы, а дружественные интерфесы или даже дружественный список методов. Ведь для взаимодействия свяазнных типов порой достаточно одного-двух методов, а классу открываются все потроха своего друга, что только усложняет поддержку кода.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, sharcUs, Вы писали:
U>В БОЛЬШИНСТВЕ случаев необходимость использования друзей свидетельствует о неправильном архитектурном решении, приводящем к усложнению объектной модели проекта и его дальнейшем сопровождении. К примеру, у меня НИ РАЗУ не появилась потребность в их использовании, хотя в прошлом уже не один завершенный проект, как на С++ так и на С#. Как правило в 98% процентах случаев включение и наследование вполне достаточно для решения требуемых задач. U>Вместе с тем я не отрицаю, что в исключительных случаях использование друзей является ЕДИНСТВЕННО ВОЗМОЖНЫМ решением, однако ДЕЙСТВИТЕЛЬНАЯ необходимость их использования ограничивается единичными случаями, как и говорит Стив в приведенной мной цитате выше. Как правило потребность в дружественных сущностях является ОШИБКОЙ в архитектуре программной модели, от которой следует избавляться и использовать вместо нее другие подходы.
class O
{
};
class I
{
};
class C
{
public:
I GetIterator();
private:
vector<O> m_elems;
int Count();
O GetElem(int index);
};
Я хочу, чтобы класс C предоставлял итератор. При этом детали (Count и GetElem) раскрывать не хочу.
Всё должно быть гибко — у меня может быть несколько итераторов на все случаи жизни (фабрики классов и т.п.).
Т.е. в одном случае обход по порядку, во втором обратный, а в третьем вообще через один или по маске. Т.е. жёстко задать Count и GetItem нельзя.
Здравствуйте, nikov, Вы писали:
N>Дружественные (friend) классы — полезная ли это концепция? И почему в .NET от нее отказались?
Не совсем отказались, вот навскидку пара аналогов:
1. В .Net есть способ ограничение видимости в пределах сборки, (internal в C#), соответственно internal класс, или internal члены публичных классов являются дружественными всем классам сборки.
2. Вложенные классы по-умолчанию являются дружественными объемлющему классу.
В базовом фреймворке процент использования этой "концепции" зашкаливает за 50%, по моему субъективному наблюдению. Т.е. кол-во internal типов не уступает кол-ву публичных типов.
Здравствуйте, vdimas, Вы писали: V>В базовом фреймворке процент использования этой "концепции" зашкаливает за 50%, по моему субъективному наблюдению. Т.е. кол-во internal типов не уступает кол-ву публичных типов.
Гм. Не совсем так: интернальность классов никакого отношения к "дружбе" не имеет. Классы внутри сборки не имеют никакого особого доступа к членам интернал-классов.
А интернал поля (которые были бы аналогом friend+private) применяются гораздо-гораздо реже.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, igna, Вы писали:
A>>Вопрос: как мне это реализовать без friend? I>Покажи, как ты это реализуешь с friend-ом.
class O
{
};
class I;
class C
{
public:
I GetIterator();
private:
vector<O> m_elems;
int Count();
O GetElem(int index);
friend class I;
};
class I
{
public:
void Reset()=0;
bool IsNext(){=0;
O Current()=0;
protected:
int Count(){return c->Count();}
O GetElem(int index){return c->GetElem(index);}
private:
C* c;
};
Реализация конкретного итератора:
class I1 : I
{
public:
I1():m_index(0){}
void Reset(){m_index = 0;}
bool IsNext(){return m_index<I::Count();}
O Current(){return I::GetElem(m_index);}
private:
int m_index;
};
В итоге о внутренней структуре знает только класс I, и немного его наследники.
Остальные знают только об интерфейсе итератора.
Здравствуйте, alzt, Вы писали:
A>В итоге о внутренней структуре знает только класс I, и немного его наследники. A>Остальные знают только об интерфейсе итератора.
А в чём преимущество этой архитектуры по сравнению с реализацией итератора прямо в C и его наследниках?
Т.е. вопрос, зачем вы разделяете класс I и класс C? Почему не перенести функциональность класса I в класс C?