Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

ООП. Использование вложенных классов.

87
27 февраля 2010 года
Kogrom
2.7K / / 02.02.2008
Меня интересует, какую выгоду приносят вложенные классы с точки зрения ООП, какое им может быть применение. Нужны ли они для ООП? У меня уже есть некоторые соображения, но не уверен что они верны. Или может кто-то видел хорошую статью статью по этому поводу?

Чтобы было понятнее, о чём речь, приведу мои рассуждения.

Например, возьмём классический пример. У автомобиля Ока имеется 4 колеса. И это колёса не просто класса Колесо, и не класса Хаммер::Колесо, а класса Ока::Колесо. То есть, так вроде бы повышается читаемость кода.

Хотя может тут можно было бы определение класса Колеса просто поместить в тот же модуль (пространство имен), в котором находится класс Ока.

Или другой пример. У определенной детали есть параметры: материал, цвет, размеры. Но размеры - это не только ширина и длина, а ещё и размеры фасок, отверстий и т.д. То есть под размеры можно выделить специальный внутренний класс Деталь::Размеры.

Тогда до создания объекта деталь мы можем создать объект размеры типа Деталь::Размеры и передать конструктору класса Деталь. Так мы будем уверены, что переданы нужные размеры.

Хотя с другой стороны, возможно, лучше сделать размеры не привязанными к конкретному виду детали.

Или грубоватый пример. Допустим у нас есть громоздкий класс с кучей однообразных настроек. Можно разбить эти настройки на группы, а группы упаковать во внутренние классы. Тогда конструктору главного класса передадим объекты этих внутренних классов, а не громадное количество настроек, что позволит повысить читаемость конструктора. Хотя при этом захламим сам класс определениями внутренних классов.
11
27 февраля 2010 года
oxotnik333
2.9K / / 03.08.2007
просто внутренний член родительского класса, от функционала которого требуется больше, нежели могут предоставить простые типы данных, повышает читабельность кода. ИМХО
5
28 февраля 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: Kogrom
Меня интересует, какую выгоду приносят вложенные классы с точки зрения ООП, какое им может быть применение. Нужны ли они для ООП?


Если относиться ко внешнему классу как к области видимости (особый случай пространства имен), то ответ тривиален: они такие же классы как и остальные.

87
28 февраля 2010 года
Kogrom
2.7K / / 02.02.2008
Цитата: oxotnik333
просто внутренний член родительского класса, от функционала которого требуется больше, нежели могут предоставить простые типы данных, повышает читабельность кода. ИМХО


Возможно, ты путаешь классы и объекты. Понятно, что в крупном объекте должно находиться несколько более мелких. Но при этом определение мелких классов может быть снаружи. Зачем определение класса помещают внутрь другого?

Цитата: hardcase
Если относиться ко внешнему классу как к области видимости (особый случай пространства имен), то ответ тривиален: они такие же классы как и остальные.


Я и не говорю, что они другие. Просто не могу понять выгоду размещения одного класса в другом. Зачем превращать класс в пространство имён?

В принципе, ещё можно понять, если внутренний класс не виден снаружи. Тут мы получаем какую-то дополнительную инкапсуляцию. А если виден, то какая выгода? Вроде бы так только код усложняется. Но ведь применяют такие классы зачем-то.

5
28 февраля 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: Kogrom
В принципе, ещё можно понять, если внутренний класс не виден снаружи. Тут мы получаем какую-то дополнительную инкапсуляцию. А если виден, то какая выгода? Вроде бы так только код усложняется. Но ведь применяют такие классы зачем-то.


Внутренние контейнеры, например контейнер для элементов стека или двоичного дерева. Их незачем выставлять наружу, так как они тесно связаны со внутренней логикой класса стека и дерева соответственно.

87
28 февраля 2010 года
Kogrom
2.7K / / 02.02.2008
Для hardcase.
Говорится о случае, когда внутренний класс имеет доступ к данным внешнего? Или просто подтверждается мысль о преимуществах невидимых снаружи внутренних классах?

Да и выгода в сокрытии таких классов не очевидна. Ну пусть кто хочет делает объекты такого класса. Тут исключение будет, если класс содержит статические данные.
253
28 февраля 2010 года
Proger_XP
1.5K / / 07.08.2004
Лично я обычно использую вложенные классы, когда разбиваю крупный объект на части. Причём такие классы не обязательно в роли namespace'ов.

Простой пример. Есть у меня класс, ищущий все файлы (с рекурскией) по маске. Внешний мир обращается к нему с помощью метода Next.
Я создаю этому классу внутреннее свойство, скажем, Iterator, где хранится вся подсобная инфа о его текущем положении в дереве файлов. Сам класс как бы обёртка для этого свойства, а этот Iterator - нечто вроде стёка.

Ну вот, сам класс TInerator вполне можно сделать подклассом, потому как использовать его где-либо ещё смысла нет - его внутреннее устройство может быть завязано на устройство основного класса, плюс не исключено, что в один прекрасный день Iterator не разделится ещё на подклассы или станет вовсе ненужным.

В общем, что и имел в виду hardcase.
5
28 февраля 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: Kogrom

Да и выгода в сокрытии таких классов не очевидна. Ну пусть кто хочет делает объекты такого класса. Тут исключение будет, если класс содержит статические данные.


Ну тогда наверно нет смысла в private модификаторах - пускай их вызвают, исключение же будет. ;)
Если говорить о контейнерных классах стека или двоичного дерева, то они нужны исключительно для работы стека или двоичного дерева. Это инфраструктурные классы и семантика их использования локализована внутри означенных классов. Расширяя это утверждение можно сказать, что если использование некоторого класса А локализовано внутри другого класса Б, то разумно класс А внутри Б и объявить.

87
28 февраля 2010 года
Kogrom
2.7K / / 02.02.2008
Цитата: Proger_XP
Лично я обычно использую вложенные классы, когда разбиваю крупный объект на части. Причём такие классы не обязательно в роли namespace'ов.


То есть был большой класс, мы стали его переделывать и часть данных и методов перенесли в другой класс, закрытый в первом. Но если эти классы так жестко связаны, что не могут жить друг без друга, то не совсем ясно, что даёт такое разделение. Возможности повторного использования кода не прибавляет. Читаемость даёт? Не уверен. Вот примеры на псевдоязыке:

1. Пример с вложенными классами:

Код:
Большой класс
{
Маленький класс
{
данное
данное
....
данное
метод
метод
...
метод
}
данное Маленького Класса
данное
данное
....
данное
метод
метод
...
метод
}


2. Теперь без вложенного:
Код:
Маленький класс
{
данное
данное
....
данное
метод
метод
...
метод
}

Большой класс
{
данное Маленького Класса
данное
данное
....
данное
метод
метод
...
метод
}

Специально не делал отступов, чтобы было нагляднее, что в первом случае данные и методы Большого Класса теряются из-за определения Маленького вложенного класса. Читаемость теряется. Если классы определены раздельно, то мое субъективное мнение - читаемость лучше.

Цитата: hardcase
Ну тогда наверно нет смысла в private модификаторах - пускай их вызвают, исключение же будет. ;)


Чарли Беббит пошутил...

Цитата: hardcase
Расширяя это утверждение можно сказать, что если семантика использования некоторого класса локализована внутри этого класса, то разумного его внутри этого же класса и объявить.


Я ничего не понял :)

253
28 февраля 2010 года
Proger_XP
1.5K / / 07.08.2004
Цитата:
То есть был большой класс, мы стали его переделывать и часть данных и методов перенесли в другой класс, закрытый в первом.


Нет, они не связаны жестко (иначе, действительно, смысла их разносить нет). Подкласс не ссылается на методы основого класса, вообще не знает о нём.
Прицепил код, может, прояснит.
(TFMStack можно считать внутренним; в Delphi 7 внутренних классов нет, в старших версиях они появились, но мне нужен был совсместимый код именно с 7.)

Цитата: hardcase
Расширяя это утверждение можно сказать, что если семантика использования некоторого класса локализована внутри этого класса, то разумного его внутри этого же класса и объявить.
[QUOTE=Kogrom;315638]Я ничего не понял :)


[/QUOTE]
hardcase, по-моему, пытался сказать, что если внутренний класс исполдьзуется для нужд основного, и знать о нём внешнему миру ни к чему, то разумно его считать просто сложным свойством этого класса.

87
28 февраля 2010 года
Kogrom
2.7K / / 02.02.2008
Цитата: Proger_XP
Прицепил код, может, прояснит.
(TFMStack можно считать внутренним; в Delphi 7 внутренних классов нет, в старших версиях они появились, но мне нужен был совсместимый код именно с 7.)


Не, не проясняет. Считать можно что угодно, но тут нет вложенных классов, насколько я понимаю. Поэтому всё читаемо.

Цитата: Proger_XP
hardcase, по-моему, пытался сказать, что если внутренний класс исполдьзуется для нужд основного, и знать о нём внешнему миру ни к чему, то разумно его считать просто сложным свойством этого класса.



Считать можно. Для понятности того, что они связаны, можно разместить их в одном модуле (как в примере). Но не пойму всё-таки зачем определение одного втаскивать в другой.

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

В C++, например, классы не обладают такими свойствами (вероятно, в C#, Java, Delphi - тоже), но фичу оставили на всякий случай.

287
28 февраля 2010 года
Shiizoo
958 / / 14.03.2004
Пример из Java на тему скрытого вложенного класса.
Класс JScrollPane (панель прокрутки) имеет вложенный protected класс JScrollPane.ScrollBar, наследующий невложенный JScrollBar. Объект этого вложенного класса используется классом JScrollPane по умолчанию. В нем переопределены пара методов, вычисляющие шаг прокрутки, так чтобы делегировать эти вычисления классу компонента, прокрутка которого собственно и осуществляется, если он может эти вычисления сделать (наследует интерфейс Scrollable). Вне класса JScrollPane этот внутренний класс не нужен никому, т. к. переопределенные методы завязаны на JScrollPane. В то же время можно извне передать этому классу другого наследника невложенного JScrollBar, переопределив таким образом поведение по умолчанию. Удобно, красиво и понятно. Вроде=)
JavaSE 6 API - javax/swing/JScrollPane

Зачем могут понадобиться public вложенные класса представить не могу.
5
01 марта 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: hardcase
Расширяя это утверждение можно сказать, что если семантика использования некоторого класса локализована внутри этого класса, то разумного его внутри этого же класса и объявить.

Писал быстро, извиняюсь за бред; исправление:

... если использование некоторого класса А локализовано внутри другого класса Б, то разумно класс А внутри Б и объявить.

Т.е. это прямое следование идее инкапсуляции.

Цитата: Kogrom
То есть был большой класс, мы стали его переделывать и часть данных и методов перенесли в другой класс, закрытый в первом.


Рефакторинг "через задницу" не рассматриваем. Я же рассказываю об изначальном проектном решении размещения типа данных там где он нужен и не выставлении кишок напоказ (инкапусляция - она самая). Shiizoo продемонстрировал другой сценарий использования, связанный со специальными наследовнием.

Цитата: Kogrom
Но если эти классы так жестко связаны, что не могут жить друг без друга, то не совсем ясно, что даёт такое разделение. Возможности повторного использования кода не прибавляет. Читаемость даёт? Не уверен. Вот примеры на псевдоязыке:

1. Пример с вложенными классами:

...
2. Теперь без вложенного:
...


Если некоторый класс объявляется внутри другого, то это нам как бы намекает уже на то, что класс этот используется исключительно внутри внешнего. Сразу локализуется контекст использования класса, так сказать "по построению". А уж как программист воспользуется (если конечно станет) этой возможностью - зависит от него. Я уже говорил об этом сценарии: инфраструктурные классы, реализующие внутренние структуры данных (очем oxotnik333 также намекнул).

Надо сказать, что я исхожу из понятий .NET (C# и Nemerle), где класс - это отдельный тип и меняться во время работы он к счастью (или к сожалению) он не может. Проблема читабельности тривиальна в контексте используемых мною языков - в них можно разбрасывать объявление класса по нескольким файлам, и, как ни странно, это помогает следовать концепции "один класс - один файл":

 
Код:
// файл OuterClass.cs

namespace My.Program {

  public partial class OuterClass : OuterBase, IOuter, ISomethingOther {
    // методы, свойства OuterClass,
    // реализация интерфейсов,
    // и конечно использование InnerClass
  }
}
Код:
// файл OuterClass_InnerClass.cs

namespace My.Program {

  partial class OuterClass {

    private class InnerClass {
      // методы, свойства InnerClass
    }

 }

}
Видно что все вполне читабельно и очевидно, а OuterClass для InnerClass выглядит просто как еще одно пространство имен.
5
01 марта 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: Kogrom
Для hardcase.
Говорится о случае, когда внутренний класс имеет доступ к данным внешнего?

В контексте .NET: внтурений класс имеет доступ к private членам внешенего класса, но основную логику все же несет внешний. Внутренний выступает некоторой вспомогательной структурой данных.

276
01 марта 2010 года
Rebbit
1.1K / / 01.08.2005
Вставлю свои 5 копеек которые косаются скорее технических моментов чем парадигмы.
Обратите внимание на нестатические внутренние классы в Java. Они держат ссылку на инстанс обьекта класса родителя, который их создал, а также имеют полный (правда псевдо) доступ к его мемберам. Использовать можно очень по разному. Класический пример - листенеры.
87
01 марта 2010 года
Kogrom
2.7K / / 02.02.2008
Цитата: hardcase
Проблема читабельности тривиальна в контексте используемых мною языков - в них можно разбрасывать объявление класса по нескольким файлам, и, как ни странно, это помогает следовать концепции "один класс - один файл"...


Ну, в общем, выглядит вполне терпимо.

Идею от Shiizoo попробую выразить по своему. В данном случае класс разделяется на 2 части. И хотя по смыслу они являются одним классом, мы пытаемся выиграть в том, что обе эти части могут наследоваться от разных внешних классов. В этом плюс.

Минус в том, что мы получаем гибридный класс, который имеет тенденцию становится громоздким.

Цитата: Rebbit
Класический пример - листенеры.



Листенеры - это не совсем то. Там создается объект некоего безымянного класса. При чём этот класс просто переопределяет какой-то метод известного внешнего класса. То есть тут больше хитрое создание уникального объекта, чем использование внутреннего класса.

276
01 марта 2010 года
Rebbit
1.1K / / 01.08.2005
Цитата: Kogrom
Листенеры - это не совсем то. Там создается объект некоего безымянного класса. При чём этот класс просто переопределяет какой-то метод известного внешнего класса. То есть тут больше хитрое создание уникального объекта, чем использование внутреннего класса.


Безымянные класы ето вообще ужос. Взять даже тот факт что тяжело создать 2 инстанса одного безымянного класа.
Кроме того между внутренним и безымянным классами есть некие различия и я предпочетаю для листенеров делать отдельные внутренние классы.
А если откинуть всякие технические уловки как то доступ до прайветов родителя то класс он и есть клас. Если внутреный то либо для подчеркивания тесной связи между ним и внешним (а ета тесначя связь в Java поддержывается также компилятором), либо для сокрытия.

87
01 марта 2010 года
Kogrom
2.7K / / 02.02.2008
Цитата: Rebbit
Безымянные класы ето вообще ужос. Взять даже тот факт что тяжело создать 2 инстанса одного безымянного класа.



Просто это использование не по назначению. Как я понимаю, безымянные классы используются для создания уникальных объектов.

11
01 марта 2010 года
oxotnik333
2.9K / / 03.08.2007
здесь мне кажется ты уже ответил на вопрос о вложенности, или взяли сомнения?
276
01 марта 2010 года
Rebbit
1.1K / / 01.08.2005
Цитата: Kogrom
Как я понимаю, безымянные классы используются для создания уникальных объектов.


Сильно сомневаюсь. Я не пробовал, но мнится мне что их и склонировать можно и рефлексией новый инстанс забабахать.
А вот ето
Java:

 
Код:
List дшіе = new ArrayList() {{add(1); add(2); add(3);}}
вообще поражает своей гениальностю
87
01 марта 2010 года
Kogrom
2.7K / / 02.02.2008
Цитата: oxotnik333
здесь мне кажется ты уже ответил на вопрос о вложенности, или взяли сомнения?



Здесь я применил, но не обосновывал. Вполне можно было вынести внутренний класс и наружу. Более того, эту структуру можно было бы тогда использовать и в других "полуумных" указателях.

87
01 марта 2010 года
Kogrom
2.7K / / 02.02.2008
Цитата: Rebbit
Сильно сомневаюсь. Я не пробовал, но мнится мне что их и склонировать можно и рефлексией новый инстанс забабахать.



Я не спорю, что можно преодолеть назначение. Я говорю про смысл.

11
01 марта 2010 года
oxotnik333
2.9K / / 03.08.2007
Цитата: Kogrom
Здесь я применил, но не обосновывал. Вполне можно было вынести внутренний класс и наружу. Более того, эту структуру можно было бы тогда использовать и в других "полуумных" указателях.


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

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог