Сообщение Re: Присвоение свойств и вызов виртуального метода из констр от 15.11.2016 7:46
Изменено 15.11.2016 7:47 netch80
Здравствуйте, sharez, Вы писали:
S>Вот век живи, век учись.
S>Оказывается в большинстве ООП языков (Java, C#, TypeScript) нет возможности присвоения пропертей до вызова конструтора суперкласса, а вызов виртуального метода из конструтора — плохая практика.
Ну если она разрешена, то на самом деле не очень и плохая Но надо учитывать особенности.
В Java, C#, а одним из первых в Object Pascal (эппловский) было так, что конструктор предка при вызове виртуального метода фактически вызывает метод потомка (того, кого сказали конструировать). В C++, наоборот, ему доступны только методы своего класса и предков, но потомков — ещё нет. Это сокращает функциональность, но повышает безопасность.
S>Стало:
S>btn = new Button();
S>btn.fill(); // не дай вам бог забыть это вызвать
S>btn.fillSomethingElse(); // а у нас наверняка и другие кишочки выпрут наружу рано или поздно
Там, где можно вызвать конструктор в виде явного конструктора, можно вызвать и что-то вроде public static Button make(), которое само вызывает конструктор (возможно, объявленный private, чтобы не показывать незавершённое состояние), вызывает нужные методы и возвращает готовый результат. Такой себе конструктор, не выглядящий синтаксически как конструктор. (В родной библиотеке Java есть множество методов с неинтуитивным названием valueOf(), которые делают что-то похожее.) После этого специфика потрохов будет скрыта только внутри данного класса и его потомков, которые должны будут отразить специфику такого формирования у себя, но не у пользователей.
По-моему, это вполне решение, которое покрывает подавляющее большинство таких ситуаций.
S>Что самое интересное, так это то, что ничего по сути не меняется, кроме нотации.
S>Ничто не мешает компилятору давать доступ хотя бы к пропертям объекта до вызова конструктора суперкласса.
S>Кто-нибудь может мне пояснить, какая же best practice для подобных случаев? И почему простое логичное решение заменено плохим нелогичным?
В том-то и дело, что "простого логичного" тут нет вообще. Есть 2-3 крайних варианта, каждый со своими тараканами, и попытка выбора компромисса между ними. Можно как в C++ — начинаем всегда с предка, предок ничего не знает про потомков. Можно как в Java, C# — начинаем всегда с предка, но через виртуальные методы можем получать дозированное знание о потомках, плюс защитные хаки вроде предварительной инициализации полей (см. ссылку от sharez). Это не позволяет заранее получить согласованное состояние потомка в целом, но позволяет делать чуть улучшений. Можно как в Python — где и как вызывается конструктор предка — чисто на совести программиста, защита есть только на уровне целостности среды исполнения. Есть ещё варианты?
S>Вот век живи, век учись.
S>Оказывается в большинстве ООП языков (Java, C#, TypeScript) нет возможности присвоения пропертей до вызова конструтора суперкласса, а вызов виртуального метода из конструтора — плохая практика.
Ну если она разрешена, то на самом деле не очень и плохая Но надо учитывать особенности.
В Java, C#, а одним из первых в Object Pascal (эппловский) было так, что конструктор предка при вызове виртуального метода фактически вызывает метод потомка (того, кого сказали конструировать). В C++, наоборот, ему доступны только методы своего класса и предков, но потомков — ещё нет. Это сокращает функциональность, но повышает безопасность.
S>Стало:
S>btn = new Button();
S>btn.fill(); // не дай вам бог забыть это вызвать
S>btn.fillSomethingElse(); // а у нас наверняка и другие кишочки выпрут наружу рано или поздно
Там, где можно вызвать конструктор в виде явного конструктора, можно вызвать и что-то вроде public static Button make(), которое само вызывает конструктор (возможно, объявленный private, чтобы не показывать незавершённое состояние), вызывает нужные методы и возвращает готовый результат. Такой себе конструктор, не выглядящий синтаксически как конструктор. (В родной библиотеке Java есть множество методов с неинтуитивным названием valueOf(), которые делают что-то похожее.) После этого специфика потрохов будет скрыта только внутри данного класса и его потомков, которые должны будут отразить специфику такого формирования у себя, но не у пользователей.
По-моему, это вполне решение, которое покрывает подавляющее большинство таких ситуаций.
S>Что самое интересное, так это то, что ничего по сути не меняется, кроме нотации.
S>Ничто не мешает компилятору давать доступ хотя бы к пропертям объекта до вызова конструктора суперкласса.
S>Кто-нибудь может мне пояснить, какая же best practice для подобных случаев? И почему простое логичное решение заменено плохим нелогичным?
В том-то и дело, что "простого логичного" тут нет вообще. Есть 2-3 крайних варианта, каждый со своими тараканами, и попытка выбора компромисса между ними. Можно как в C++ — начинаем всегда с предка, предок ничего не знает про потомков. Можно как в Java, C# — начинаем всегда с предка, но через виртуальные методы можем получать дозированное знание о потомках, плюс защитные хаки вроде предварительной инициализации полей (см. ссылку от sharez). Это не позволяет заранее получить согласованное состояние потомка в целом, но позволяет делать чуть улучшений. Можно как в Python — где и как вызывается конструктор предка — чисто на совести программиста, защита есть только на уровне целостности среды исполнения. Есть ещё варианты?
Re: Присвоение свойств и вызов виртуального метода из констр
Здравствуйте, sharez, Вы писали:
S>Вот век живи, век учись.
S>Оказывается в большинстве ООП языков (Java, C#, TypeScript) нет возможности присвоения пропертей до вызова конструтора суперкласса, а вызов виртуального метода из конструтора — плохая практика.
Ну если она разрешена, то на самом деле не очень и плохая Но надо учитывать особенности.
В Java, C#, а одним из первых в Object Pascal (эппловский) было так, что конструктор предка при вызове виртуального метода фактически вызывает метод потомка (того, кого сказали конструировать). В C++, наоборот, ему доступны только методы своего класса и предков, но потомков — ещё нет. Это сокращает функциональность, но повышает безопасность.
S>Стало:
S>btn = new Button();
S>btn.fill(); // не дай вам бог забыть это вызвать
S>btn.fillSomethingElse(); // а у нас наверняка и другие кишочки выпрут наружу рано или поздно
Там, где можно вызвать конструктор в виде явного конструктора, можно вызвать и что-то вроде public static Button make(), которое само вызывает конструктор (возможно, объявленный private, чтобы не показывать незавершённое состояние), вызывает нужные методы и возвращает готовый результат. Такой себе конструктор, не выглядящий синтаксически как конструктор. (В родной библиотеке Java есть множество методов с неинтуитивным названием valueOf(), которые делают что-то похожее.) После этого специфика потрохов будет скрыта только внутри данного класса и его потомков, которые должны будут отразить специфику такого формирования у себя, но не у пользователей.
По-моему, это вполне решение, которое покрывает подавляющее большинство таких ситуаций.
S>Что самое интересное, так это то, что ничего по сути не меняется, кроме нотации.
S>Ничто не мешает компилятору давать доступ хотя бы к пропертям объекта до вызова конструктора суперкласса.
S>Кто-нибудь может мне пояснить, какая же best practice для подобных случаев? И почему простое логичное решение заменено плохим нелогичным?
В том-то и дело, что "простого логичного" тут нет вообще. Есть 2-3 крайних варианта, каждый со своими тараканами, и попытка выбора компромисса между ними. Можно как в C++ — начинаем всегда с предка, предок ничего не знает про потомков. Можно как в Java, C# — начинаем всегда с предка, но через виртуальные методы можем получать дозированное знание о потомках, плюс защитные хаки вроде предварительной инициализации полей (см. ссылку от Sharov). Это не позволяет заранее получить согласованное состояние потомка в целом, но позволяет делать чуть улучшений. Можно как в Python — где и как вызывается конструктор предка — чисто на совести программиста, защита есть только на уровне целостности среды исполнения. Есть ещё варианты?
S>Вот век живи, век учись.
S>Оказывается в большинстве ООП языков (Java, C#, TypeScript) нет возможности присвоения пропертей до вызова конструтора суперкласса, а вызов виртуального метода из конструтора — плохая практика.
Ну если она разрешена, то на самом деле не очень и плохая Но надо учитывать особенности.
В Java, C#, а одним из первых в Object Pascal (эппловский) было так, что конструктор предка при вызове виртуального метода фактически вызывает метод потомка (того, кого сказали конструировать). В C++, наоборот, ему доступны только методы своего класса и предков, но потомков — ещё нет. Это сокращает функциональность, но повышает безопасность.
S>Стало:
S>btn = new Button();
S>btn.fill(); // не дай вам бог забыть это вызвать
S>btn.fillSomethingElse(); // а у нас наверняка и другие кишочки выпрут наружу рано или поздно
Там, где можно вызвать конструктор в виде явного конструктора, можно вызвать и что-то вроде public static Button make(), которое само вызывает конструктор (возможно, объявленный private, чтобы не показывать незавершённое состояние), вызывает нужные методы и возвращает готовый результат. Такой себе конструктор, не выглядящий синтаксически как конструктор. (В родной библиотеке Java есть множество методов с неинтуитивным названием valueOf(), которые делают что-то похожее.) После этого специфика потрохов будет скрыта только внутри данного класса и его потомков, которые должны будут отразить специфику такого формирования у себя, но не у пользователей.
По-моему, это вполне решение, которое покрывает подавляющее большинство таких ситуаций.
S>Что самое интересное, так это то, что ничего по сути не меняется, кроме нотации.
S>Ничто не мешает компилятору давать доступ хотя бы к пропертям объекта до вызова конструктора суперкласса.
S>Кто-нибудь может мне пояснить, какая же best practice для подобных случаев? И почему простое логичное решение заменено плохим нелогичным?
В том-то и дело, что "простого логичного" тут нет вообще. Есть 2-3 крайних варианта, каждый со своими тараканами, и попытка выбора компромисса между ними. Можно как в C++ — начинаем всегда с предка, предок ничего не знает про потомков. Можно как в Java, C# — начинаем всегда с предка, но через виртуальные методы можем получать дозированное знание о потомках, плюс защитные хаки вроде предварительной инициализации полей (см. ссылку от Sharov). Это не позволяет заранее получить согласованное состояние потомка в целом, но позволяет делать чуть улучшений. Можно как в Python — где и как вызывается конструктор предка — чисто на совести программиста, защита есть только на уровне целостности среды исполнения. Есть ещё варианты?