5 / 5 / 2
Регистрация: 21.07.2016
Сообщений: 34

Доступ к дочерним классам из родительского

27.07.2016, 06:14. Показов 7822. Ответов 4
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Многим известно, что родительский класс ничего не должен знать о своих потомках. Упоминание дочерних классов в родительском считается плохим тоном, нарушающим принципы ООП и т.п. (в Java я знаю только один случай такого - это класс Number). Но у меня возникла ситуация, в которой задача прекрасно решается этим самым "плохим тоном". А пишу я сюда, потому что практически уверен, что имеется иной, более правильный способ, и, быть может, вы его подскажете.

Задача самая что ни на есть типичная. Имеется список объектов, который выводится в GUI-списке (неважно, будь то ListView или TreeView). Объекты разные и потому у них очень много различающихся полей. Но все они - потомки одного класса, назовем его Animal:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
abstract class Animal {
    Property<String> name;
    Property<Double> weight;
    ...
}
 
class Bird extends Animal {
    Property<String> color;
    Property<Boolean> flying;
    Property<Double> tail_length;
    ...
}
 
class Fish extends Animal {
    Property<Integer> type;
    Property<Double> habitat_depth;
    ...
}
 
// и еще штук 20 потомков Animal
Соответственно, GUI-список создается как ListView<Animal> из смешанного массива Animal[]{new Bird(), new Fish(), new Fish(), new Worm(), new Fish(), ...}. И как только пользователь выделит в этом ListView какой-либо объект, то в программе где-нибудь справа будут выведены только те поля TextField, которые необходимы для редактирования только выделенного объекта. При этом, разумеется, происходит связка полей TextField с Property текущего объекта:

Java
1
2
3
Animal animal = list_view.getSelected(); // на самом деле метод другой, но это не суть
BidirectionalBinding.bind(text_field_weight.textProperty(), animal.weight);
BidirectionalBinding.bind(text_field_birdcolor.textProperty(), animal.color); // ошибка компиляции! unknown field 'color'
Очевидно, что на момент написания кода компилятор не знает, какой именно дочерний класс будет выделен, поэтому getSelected() всегда возвращает только Animal. Но тогда как определить какое животное сейчас выделено, чтобы получить доступ к свойствам дочерних классов?

Как делаю я. Т.к. получить можно только родительский класс, то в него я и добавляю ссылки на всех его потомков:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
abstract class Animal {
    Bird asBird = null; // !!!
    Fish asFish = null; // !!!
    ...
}
 
class Bird extends Animal {
    Bird() {
        asBird = this;
    }
}
 
class Fish extends Animal {
    Fish() {
        asFish = this;
    }
}
Таким образом, каждый потомок имеет ссылку только "на самого себя", а ссылки, ведущие на всех остальных "братьев и сестер", всегда равны null. Теперь перед биндингом остается только проверить, каким именно животным является выделенный объект, и тут же сделать необходимые связки:

Java
1
2
3
4
5
6
7
8
9
Animal animal = list_view.getSelected();
BidirectionalBinding.bind(text_field_weight.textProperty(), animal.weight);
if (animal.asBird != null) {
    BidirectionalBinding.bind(text_field_birdcolor.textProperty(), animal.asBird.color);
    ...
} else if (animal.asFish != null) {
    BidirectionalBinding.bind(text_field_fishtype.textProperty(), animal.asFish.type);
    ...
} else if ...
Просьба не придираться к терминологии или к коду (он только для примера). Вопрос у меня лишь в том, существует ли иное, более элегантное решение для доступа к полям дочерних классов, имея на входе только родительский? Или мой подход вполне оправдан для подобных случаев?
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
27.07.2016, 06:14
Ответы с готовыми решениями:

Доступ к классам апплета
В jar файле находятся кроме апплета еще два класса, так вот можно ли из JavaScript обратится к ним, если да то как??

Использование родительского метода с дочерним параметром
Добрый день. Есть 2 класса: родительский и дочерний. В родительском классе обьявлен метод который принимает родительский тип, и возвращает...

Доступ к методу родительского класса из метода свойства родительского класса
Не знаю правильно ли написал тему, ну суть такая, есть класс который описывает допустим шарик, а есть класс который описывает массив этих...

4
Автоматизируй это!
Эксперт Python
 Аватар для Welemir1
7390 / 4817 / 1246
Регистрация: 30.03.2015
Сообщений: 13,664
Записей в блоге: 29
27.07.2016, 10:51
JavaFX2, а мне понравилось твое решение, необычно!
я бы конечно сделал не в родительском классе, а в коде нечто вроде
Java
1
2
3
4
if (animal instanceof Bird) {
            Bird bird = (Bird) animal;
            bird.anotherMethod(); //метод именно класса Птиц
        }
0
Эксперт Java
 Аватар для KEKCoGEN
2399 / 2224 / 565
Регистрация: 28.12.2010
Сообщений: 8,672
27.07.2016, 11:29
Цитата Сообщение от JavaFX2 Посмотреть сообщение
Или мой подход вполне оправдан для подобных случаев?
нет, не оправдан

Цитата Сообщение от Welemir1 Посмотреть сообщение
animal instanceof Bird
использовать instanceof это такое же нарушение ООП.

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

Так же можно написать некий вспомогательный класс скажем ListViewPopulator, который будет работать с объектами интерфейса и будет знать как отображать каждый объект.

На словах объяснить довольно сложно. Попробуйте составить архитектуру, построить диаграмму классов и выложить её сюда прежде чем начнете писать код.
0
5 / 5 / 2
Регистрация: 21.07.2016
Сообщений: 34
12.08.2016, 10:22  [ТС]
Потребовалось расширить функционал программы, и заодно решил провести рефакторинг в тех местах, где он явно просится. Разумеется, вышеописанная проблема - одно из них. И честно говоря, чуть ли не единственное, требующее рефакторинга.

KEKCoGEN, я несколько раз возвращался к тому, как можно заменить явное указание дочерних классов на более-менее адекватный полиморфизм в рамках ООП. Но полностью устраивающего меня решения я так и не нашел. Наверное мне не хватило степени абстрагирования, в попытках найти решение через бесконечную цепочку вспомогательных классов/интерфейсов.

Единственное, что мне пришло в голову - это передавать переопределяемым (в дочерних классах) методам сам контроллер, в котором прописаны ВСЕ поля textfield, которые вообще участвуют в биндинге. И далее уже в каждом дочернем классе использовать те поля, которые этому дочернему классу необходимы:

Java
1
2
3
4
// файл FXML-контроллера, в котором имеются текстовые поля text_field_birdcolor, text_field_fishtype и т.п.
 
Animal animal = list_view.getSelected();
animal.bind(this); // здесь this - это и есть контроллер
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// а это непосредственно сами классы для представления данных
 
abstract class Animal {
    Property<String> name;
    Property<Double> weight;
    ...
    void bind(FXMLController controller) {
        BidirectionalBinding.bind(controller.text_field_weight.textProperty(), weight);
        ...
    }
}
 
class Bird extends Animal {
    Property<String> color;
    ...
    @Override void bind(FXMLController controller) {
        super.bind(controller);
        BidirectionalBinding.bind(controller.text_field_birdcolor.textProperty(), color);
        ...
    }
}
 
class Fish extends Animal {
    Property<Integer> type;
    ...
    @Override void bind(FXMLController controller) {
        super.bind(controller);
        BidirectionalBinding.bind(controller.text_field_fishtype.textProperty(), type);
        ...
    }
}
Чем плохо такое решение?... Мало того, что все поля контроллера необходимо делать открытыми (public), так еще и классы модели теперь обязаны "знать" всё о контроллере и то, как с ним работать. Хотя как раз именно контроллер должен оперировать данными, а не наоборот.

Первое, что напрашивается - это перевести всю логику биндинга в третий отдельный класс, который и будет связывать контроллер и классы Animal (назовем для простоты Helper) - какое-никакое, но улучшение. Только вот, что писать в контроллере после Animal animal = list_view.getSelected()? Helper.bind(controller, animal)?? Не пойдет. Ведь Helper не знает заранее, какой именно Animal выделен и мы вернулись к тому, с чего начали - к проблеме определения текущего подкласса. Значит надо делать как-то иначе, а все размышления по поводу поведенческих интерфейсов и ListViewPopulator мне ничего не дали... Так что тут у меня кончились идеи и поэтому буду благодарен, если кто поделится своими.
0
Автоматизируй это!
Эксперт Python
 Аватар для Welemir1
7390 / 4817 / 1246
Регистрация: 30.03.2015
Сообщений: 13,664
Записей в блоге: 29
12.08.2016, 22:27
JavaFX2, а не думал в родительском абстрактном классе реализовать просто все? все методы и поля прописать -хвосты, рога, плавники, этакий суперпредок всех многоклеточных . А уже в конкретных реализациях оверрайдить нужные методы и полям что то присваивать, остальное оставляя пустым? Соответственно при выборе кого то из потомков нам не важно кто это, просто все не пустые поля выводим.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
12.08.2016, 22:27
Помогаю со студенческими работами здесь

Доступ к дочерним объектам TableView
Как получить доступ к дочерним объектам TableView ?

Доступ к дочерним объектам в TabView в Qml Qt
Добрый день! Помогите пожалуйста решить проблему. Не могу получить доступ к модели, которая расположена в TabView. Посмотрела по...

Доступ к дочерним элементам скопированного элемента
Скоировал div, в котором есть элемент(ы). var copyElement=document.getElementsByClassName(&quot;className&quot;).cloneNode(true); но не...

CSS: доступ к классам
Хотела бы из скрипта получать значение свойства color, заданное в определенном классе таблицы стилей. Описанный в учебнике...

Выборочный доступ к классам.
Здравствуйте, допустим, что у меня есть класс General, как мне сделать так, чтобы некоторые левые классы имели доступ к методам класса...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
5
Ответ Создать тему
Опции темы

Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Access
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru