Заміна на приклад Java?


10

Тож я досить новачок у програмуванні в реальному світі (за межами академічних проектів) і натрапив на безліч дописів, у яких говориться, що використовувати instanceofце погано, щоб визначити, для якого класу конкретний об’єкт.

Моя ситуація полягає в тому, що у мене є три класи, базовий клас продуктів, той, який розширює цей, і інший, який продовжує це. Усі вони зберігаються в одній таблиці в базі даних, і у мене є якийсь код, який повинен використовувати методи на кожній, щоб витягувати дані з них.

Яка найкраща практика обійти цей спосіб? Я прочитав деякі речі про поліморфізм, але не можу знайти жодного прикладу, який би вирішив цю проблему. Зазвичай вони замінюють метод, який для мене не буде працювати, оскільки мені потрібно тягнути різні речі з різних об'єктів.

Чи є кращий спосіб зробити це чи я застряг із використанням instanceofчи якимось відображенням, щоб отримати специфічні для об’єктів поля?


10
Справа не в тому, що ключове слово instanceofнеправильне; намагання знайти клас об’єкта, як правило, є проблемою. Не завжди помиляється, але, мабуть, у вашому випадку це так. Можливо, якщо ви скажете нам, що ви намагаєтеся досягти, ми можемо запропонувати рішення, використовуючи поліморфізм.
Андрес Ф.

2
Гаразд, кожен клас має специфічні для нього дані. Я хочу витягнути з них конкретні дані. Я думаю, що я зрозумів відповідь за допомогою, яку я мав. Щось на зразок методу, який називається, getSpecifics()який реалізується по-різному на кожному, при цьому кожен повертає дані, специфічні для класу?
Томас Мітчелл

Відповіді:


16

Причина instanceofвідлякує в тому, що це не OOP.

Не повинно бути причин для того, щоб абонент / користувач об'єкта знав, який конкретний клас - це екземпляр, поза яким типом змінної він оголошений як.

Якщо вам потрібна інша поведінка в підкласах, додайте метод та реалізуйте їх по-різному.


1
Отже, метод getSpecifics()(або щось подібне) для кожного класу, який поверне специфіку для кожного класу. Це найкращий підхід?
Томас Мітчелл

@Schmooo Вам дійсно потрібно задуматися про те, де існує загальна функціональність у дереві класів та які контракти існують на кожному рівні.

3
А як щодо випадку, коли ваш об’єкт перетинає шари і інформація про тип втрачається? Приклад я створюю List. Я передаю його об'єкту, який бере будь-який Iterable. Тепер цей другий об'єкт передає його третьому об'єкту, який приймає або Listдля оптимізації, або, Iterableале набагато повільніше. Другий не повинен знати, що це його список, але третій дуже хотів би знати. Чи не повинен третій об'єкт перевірити, наприклад, щоб побачити, чи може він застосувати оптимізацію? Дивіться, наприклад, гуаву, FluentIterableяка робить саме це.
Лоран Буро-Рой

1
Я б сказав, що це не завжди так, що вам потрібно додати метод до класу. Наприклад, якийсь код отримує виняток і повинен робити щось з ним залежно від його типу. Скажімо, складає звіт або щось робить, щоб відновити помилку. Цей вид функціональності, безумовно, не підходить до винятків. Або, якщо класи походять з якоїсь зовнішньої бібліотеки, то у вас навіть немає засобів для зміни цих класів. У цьому випадку я думаю, що цілком посилатися на instanceof.
Малькольм

9

instanceof це не обов'язково погана річ, однак це те, на що варто звернути увагу.

Приклад того, як вона працює правильно, - це місце, де ви отримуєте колекцію базового типу, а вам потрібні лише підтипи. Отримання мережевих адрес з NetworkInterface.getNetworkInterfaces()повернення назад об’єктів NetworkInterface, у яких є колекція об'єктів InetAddress, частина з яких - Inet4Address, а частина - Inet6Address. Якщо ви хочете відфільтрувати колекцію для об’єктів Inet4Address, необхідно використовувати instanceof.

У ситуації, що описується в початковій публікації, є клас Base, щось, що розширює цей базовий клас, і щось, що розширює розширений клас. Хоча це не зовсім інформативно, але, здається, є основою дещо менш, ніж ідеальний дизайн.

Коли ви повертаєте базовий клас, якщо немає вагомих причин, щоб він був розроблений таким чином (зворотна сумісність між специфікаціями попередньої версії), ви не повинні намагатися зазирнути до базових типів. Якщо вам повернули набір, ви знаєте, що отримуєте набір. Це дозволяє розробнику пізніше змінити свою думку, повернути більш конкретний тип (SortedSet) або змінити базовий тип (HashSet на TreeSet), нічого не порушуючи.

Перегляньте свій дизайн того, як структуровані та паректировані об'єкти, щоб побачити, чи можна зробити кращу модель класу, яка не потребує розрізнення типів.


Цілком можливо, що встановлення інтерфейсу isOfType(SomeEnum.IPv4)методу може бути кращим способом фільтрації за такими якостями, ніж перевірка конкретного типу через instanceof. Що робити, якщо ви хочете пізніше розділити свій клас реалізації IPv4? Не те, що це завжди краще, але це врахування.
Стівен Шланскер

@StevenSchlansker Це могло б працювати для конкретного класу. Але що робити, якщо потрібно перевірити конкретний тип, щоб побачити, чи можливий виступ (або чи просто викинути і вилучити виняток?) Inet6Address реалізує більше методів, ніж InetAddress. Йому буде важко працювати з інтерфейсами (enum, який перераховує кожен клас у пакеті? Або завантажувач класів?), І це означатиме, що метод повинен бути реалізований вручну (або якийсь код відображення в Object). Розглянемо, як це зробити (o instanceof Serializable) || (o instanceof Externalizable). instanceof кращий за альтернативу

1

Можна використовувати метод getClass ().

Ви впевнені, що вам потрібні три різні класи? Можливо, один клас із перемикачем всередині буде служити краще?


7
getClassі instanceofділитися недоліками. Поліморфізм кращий, ніж обидва, коли він підходить, і я не вважаю, що це не відповідає випадку використання ОП.

Я не маю нічого проти вашого першого речення. Але другий - вибачте, я не можу зрозуміти, що ти взагалі маєш на увазі.
Gangnus

1
З getClassя все ще не поділяю ту саму проблему, що й користування instanceof? Мені все ж доведеться з’ясувати, що я маю, а потім назвати набір функцій. В ідеалі я хочу метод, який повертає дані, специфічні для цього класу, не потребуючи передачі на цей об’єкт.
Томас Мітчелл

Я скаржусь на те, що ви ігноруєте поліморфізм, оскільки це часто кращий вибір. Я майже впевнений, що випадок використання у запитанні може бути зроблений при поліморфізмі, тому я не бачу потреби в використанні. (Окрім цього, просто відтворити чужу відповідь не приємно.)

2
@Gangus Я не вважаю це "нападом", я не маю на увазі жодного образи. Але що б там не було. Ні, питання не в тому, "що ще можна використовувати", це "чи є кращий спосіб зробити це". Якщо ви не хочете сперечатися getClass, це кращий спосіб зробити це, у нього немає жодної справи, яка займає більшість відповідей ;-)

1

Як правило, коли мені здається, що я хочу знати тип чогось, це означає, що я неправильно реалізував свою об'єктну структуру. Більшість цих часів зводиться до порушення LSP .

Однак бувають випадки, коли я хочу, щоб я мав можливість зробити динамічну відправку і заощадити тонну кодової тарілки та захистити свою майбутню структуру. C # забезпечує динамічне ключове слово в нових партіях фреймворку, але, наскільки я знаю, у Java все ще немає чогось подібного.

Однак, instanceof, як правило, краще, ніж порівняння класів, оскільки він належним чином підтримуватиме спадкування. Ви також можете використовувати такі методи, як isAssignableFrom та інші з API рефлексії. Якщо ви хочете реалізувати щось на зразок динамічної диспетчеризації, це можна зробити через API відбиття, але будьте обережні, це буде повільно. Використовуйте з обережністю, в ідеалі ви повинні виправити структуру об'єкта та дизайн своєї програми, якщо можете.

Сподіваюсь, це допомагає

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.