Как зайти в Даркнет?!
25th January, 01:11
8
0
Как в tkinter из поля ввода Entry получить значение в одну переменную и обновить строку кнопкой, затем получить ещё одно введённое значение и затем сложить их. Ниже пример кода
21st July, 19:00
899
0
Программа, которая создает фейковые сервера в поиске игровых серверов CS 1.6 Steam
21st March, 17:43
952
0
Очень долго работает Update запрос Oracle
27th January, 09:58
916
0
не могу запустить сервер на tomcat HTTP Status 404 – Not Found
21st January, 18:02
907
0
Где можно найти фрилансера для выполнения поступающих задач, на постоянной основе?
2nd December, 09:48
942
0
Разработка мобильной кроссплатформенной военной игры
16th July, 17:57
1727
0
период по дням
25th October, 10:44
3957
0
Пишу скрипты для BAS только на запросах
16th September, 02:42
3722
0
Некорректный скрипт для закрытия блока
14th April, 18:33
4614
0
прокидывать exception в блоках try-catch JAVA
11th March, 21:11
4382
0
Помогите пожалуйста решить задачи
24th November, 23:53
6087
0
Не понимаю почему не открывается детальное описание продукта
11th November, 11:51
4352
0
Нужно решить задачу по программированию на массивы
27th October, 18:01
4400
0
Метода Крамера С++
23rd October, 11:55
4309
0
помогите решить задачу на C++
22nd October, 17:31
4002
0
Помогите решить задачу на python с codeforces
22nd October, 11:11
4492
0
Python с нуля: полное руководство для начинающих
18th June, 13:58
2599
0
Почему вы должны препятствовать тому, чтобы класс был подклассом?
Какие могут быть причины, чтобы предотвратить наследование класса? (например, использование запечатанного на классе c#) Прямо сейчас я ничего не могу придумать.
Как насчет того, если вы еще не уверены в интерфейсе и не хотите, чтобы какой-либо другой код зависел от текущего интерфейса? -"Это не в моей голове, но меня интересуют и другие причины!]
Редактировать: Немного погуглив дал следующее: http://codebetter.com/blogs/patricksmacchia/archive/2008/01/05/rambling-on-the-sealed-keyword.aspx
Цитирование:
Есть три причины, почему запечатанный класс лучше, чем незапечатанный класс:
- Управление версиями: когда класс изначально запечатан, он может измениться на незапечатанный в будущем без нарушения совместимости. (…)
- Производительность: ( ... ) если компилятор JIT видит вызов виртуального метода с использованием запечатанных типов, компилятор JIT может создать более эффективный код, вызвав метод не виртуально.(…)
- Безопасность и предсказуемость: класс должен защищать свое собственное государство и никогда не позволять себе стать коррумпированным. Когда класс не запечатан, производный класс может получить доступ и управлять состоянием базового класса, если любые поля данных или методы, которые внутренне управляют полями, доступны и не являются частными.(…)
Я хочу передать вам это сообщение от "Code Complete":
Наследование-подклассы-имеет тенденцию к работа против первичного технического необходимо вы, как программист, то есть управлять сложностью. Для того чтобы контролировать сложность, вы должны поддерживать сильное предубеждение против наследования.
Единственное законное использование наследования - это определение конкретного случая базового класса, например, когда наследуется от формы к производному кругу. Чтобы проверить это, взгляните на отношение в противоположном направлении: является ли форма обобщением круга? Если ответ положительный, то можно использовать наследование.
Поэтому если у вас есть класс, для которого не может быть никаких частных случаев, которые специализируют его поведение, он должен быть запечатан.
Кроме того, благодаря LSP (Принцип подстановки Лискова) можно использовать производный класс, где ожидается базовый класс, и это на самом деле оказывает наибольшее влияние от использования наследования: код, использующий базовый класс, может быть передан наследуемому классу, и он все равно должен работать так, как ожидалось . Чтобы защитить внешний код, когда нет очевидной необходимости в подклассах, вы запечатываете класс, и его клиенты могут быть уверены, что его поведение не будет изменено. В противном случае внешний код должен быть явно разработан, чтобы ожидать возможных изменений в поведении подклассов.
Более конкретным примером может служить шаблон Singleton. Вам нужно запечатать singleton, чтобы убедиться, что вы не можете сломать "singletonness".
Это может не относиться к вашему коду, но многие классы в рамках фреймворка .NET намеренно запечатаны, чтобы никто не пытался создать подкласс.
Есть определенные ситуации, когда внутренние компоненты сложны и требуют очень специфического управления определенными вещами, поэтому дизайнер решил, что никто не должен наследовать класс, чтобы никто случайно не нарушил функциональность, используя что-то неправильно.
@jjnguy
Другой пользователь может захотеть повторно использовать ваш код, подклассировав ваш класс. Я не вижу причин останавливать это.
Если они хотят использовать функциональность моего класса, они могут достичь этого с помощью сдерживания, и в результате у них будет гораздо менее хрупкий код.
Композиция, кажется, часто упускается из виду; слишком часто люди хотят вскочить на подножку наследства. Они не должны этого делать! Заменяемость-это сложно. По умолчанию к композиции; вы будете благодарить меня в конечном счете.
Я согласен с джейнгуем... Я думаю, что причин для того, чтобы запечатать класс, очень мало и далеко между ними. Совсем наоборот, я не раз оказывался в ситуации, когда хотел расширить класс, но не мог, потому что он был запечатан.
В качестве идеального примера я недавно создал небольшой пакет (Java, а не C#,, но те же принципы), чтобы обернуть функциональность вокруг инструмента memcached. Мне нужен был интерфейс, чтобы в тестах я мог издеваться над memcached клиентом API, который я использовал, а также чтобы мы могли переключать клиентов, если возникнет необходимость (есть 2 клиента, перечисленных на главной странице memcached). Кроме того, я хотел бы иметь возможность полностью заменить функциональность, если возникнет необходимость или желание (например, если серверы memcached по какой-то причине не работают, мы могли бы потенциально заменить их на локальную реализацию кэша).
Я представил минимальный интерфейс для взаимодействия с клиентом API, и было бы здорово расширить класс client API, а затем просто добавить предложение implements с моим новым интерфейсом. Методы, которые я имел в интерфейсе, который соответствовал фактическому интерфейсу, тогда не нуждались бы в дополнительных деталях, и поэтому мне не пришлось бы явно реализовывать их. Тем не менее, класс был запечатан, поэтому мне пришлось вместо прокси вызывать внутреннюю ссылку на этот класс. Результат: больше работы и намного больше кода без какой-либо реальной причины.
Тем не менее, я думаю, что есть потенциальные моменты, когда вы можете захотеть сделать класс запечатанным... и самое лучшее, что я могу придумать, - это API, который вы будете вызывать напрямую, но позволите клиентам реализовать. Например, игра, где вы можете запрограммировать против игры... если ваши классы не были запечатаны, то игроки, добавляющие функции, могут потенциально использовать API в своих интересах. Однако это очень узкий случай, и я думаю, что в любое время, когда вы полностью контролируете кодовую базу, на самом деле мало, если вообще есть причина, чтобы сделать класс закрытым.
Это одна из причин, по которой мне очень нравится язык программирования Ruby... даже основные классы открыты не только для расширения, но и для динамического добавления и изменения функциональности самого класса! Это называется обезьяньей охотой и может быть кошмаром, если злоупотреблять ею, но играть с ней чертовски весело!
С объектно-ориентированной точки зрения, запечатывание класса четко документирует намерение автора без необходимости комментариев. Когда я запечатываю класс, я пытаюсь сказать, что этот класс был разработан, чтобы инкапсулировать какую-то конкретную часть знаний или какую-то конкретную услугу. Он не был предназначен для дальнейшего расширения или подклассов.
Это хорошо сочетается с шаблоном метода design pattern. У меня есть интерфейс, который говорит "I perform this service.", а затем у меня есть класс, который реализует этот интерфейс. Но что, если выполнение этой службы зависит от контекста, о котором базовый класс не знает (и не должен знать)? Дело в том, что базовый класс предоставляет виртуальные методы, которые являются либо защищенными, либо закрытыми, и эти виртуальные методы являются крючками для подклассов, чтобы предоставить часть информации или действия, которые базовый класс не знает и не может знать. Между тем базовый класс может содержать код, общий для всех дочерних классов. Эти подклассы будут запечатаны, потому что они предназначены для выполнения этой одной и только одной конкретной реализации службы.
Можете ли вы привести аргумент, что эти подклассы должны быть дополнительно подклассированы, чтобы улучшить их? Я бы сказал "нет", потому что если этот подкласс не мог выполнить работу в первую очередь, то он никогда не должен был быть производным от базового класса. Если вам это не нравится, то у вас есть оригинальный интерфейс, идите и напишите свой собственный класс реализации.
Герметизация этих подклассов также препятствует глубоким уровням наследования, что хорошо работает для фреймворков GUI, но плохо работает для уровней бизнес-логики.
Большинство ответов (когда они абстрагированы) утверждают, что запечатанные/завершенные классы являются инструментом для защиты других программистов от потенциальных ошибок. Существует размытая грань между значимой защитой и бессмысленным ограничением. Но пока программист-это тот, кто должен понимать программу, я не вижу почти никаких причин, чтобы ограничить его от повторного использования частей класса. Большинство из вас говорит о занятиях. Но ведь все дело в предметах!
В своем первом посте DrPizza утверждает, что проектирование наследуемого класса означает предвидение возможных расширений. Правильно ли я понимаю, что вы считаете, что класс должен быть наследуемым только в том случае, если он, вероятно, будет хорошо расширен? Похоже, что вы привыкли разрабатывать программное обеспечение из самых абстрактных классов. Позвольте мне кратко объяснить, как я думаю при проектировании:
Отталкиваясь от очень конкретных объектов, я нахожу общие для них характеристики и [таким образом] функциональность и абстрагирую их до суперкласса этих конкретных объектов. Это способ уменьшить дублирование кода.
Если вы не разрабатываете какой-то конкретный продукт, такой как фреймворк, я должен заботиться о своем коде, а не о другом (виртуальном) коде. Тот факт, что другие могут счесть полезным повторно использовать мой код, является приятным бонусом, а не моей основной целью. Если они решат сделать это, то это их обязанность-обеспечить действительность продлений. Это касается всей команды. Передний дизайн имеет решающее значение для производительности.
Возвращаясь к моей идее: ваши объекты должны в первую очередь служить вашим целям, а не какой-то возможной shoulda/woulda/coulda функциональности их подтипов. Ваша цель-решить данную проблему. Объектно-ориентированные языки используют тот факт, что многие проблемы (или, скорее всего, их подзадачи) схожи, и поэтому существующий код может быть использован для ускорения дальнейшей разработки.
Запечатывание класса заставляет людей, которые могли бы воспользоваться преимуществами существующего кода, фактически не изменяя ваш продукт, заново изобретать колесо. (Это ключевая идея моего тезиса: наследование класса не изменяет его! Что кажется вполне естественным и очевидным, но это обычно игнорируется).
Люди часто боятся, что их "open" классы будут скручены к чему-то, что не может заменить своих восходящих предков. Ну и что с того? Почему тебя это должно волновать? Ни один инструмент не может помешать плохому программисту создавать плохое программное обеспечение!
Я не пытаюсь обозначить наследуемые классы как в конечном счете правильный способ проектирования, рассматривая это скорее как объяснение моей склонности к наследуемым классам. В этом вся прелесть программирования-практически бесконечный набор правильных решений, каждое из которых имеет свои плюсы и минусы. Ваши комментарии и аргументы приветствуются.
И, наконец, мой ответ на первоначальный вопрос: я бы доработал класс, чтобы другие знали, что я считаю класс листом иерархического дерева классов, и я не вижу абсолютно никакой возможности, что он может стать родительским узлом. (И если кто-то думает, что это действительно может быть, то либо я ошибся, либо они меня не понимают).
Потому что вы всегда хотите получить ссылку на класс а не на производный по разным причинам:
I. инварианты, которые вы имеете в какой-то другой части вашего кода
второй. безопасности
и т.д.
Кроме того, потому что это безопасная ставка в отношении обратной совместимости - вы никогда не сможете закрыть этот класс для наследования, если он не будет открыт.
Или, возможно, у вас не было достаточно времени, чтобы проверить интерфейс, который предоставляет класс, чтобы быть уверенным, что вы можете позволить другим наследовать от него.
Или, может быть, нет никакого смысла (что вы видите сейчас) в том, чтобы иметь подкласс.
Или вам не нужны отчеты об ошибках, когда люди пытаются подклассировать и не успевают получить все мелкие детали-сократите расходы на поддержку.
Иногда интерфейс вашего класса просто не предназначен для того, чтобы быть инхеирированным. Публичный интерфейс просто не является виртуальным, и хотя кто-то может переопределить существующую функциональность, это будет просто неправильно. Да, в общем случае они не должны переопределять открытый интерфейс, но вы можете гарантировать, что они этого не сделают, сделав класс не наследуемым.
Пример, который я могу придумать прямо сейчас, - это настроенные содержащиеся классы с глубокими клонами в .Net. Если вы наследуете от них, вы теряете способность глубокого клонирования.[Я немного расплывчат в этом примере, это было некоторое время с тех пор, как я работал с IClonable] если у вас есть истинный класс singelton, вы, вероятно, не хотите, чтобы наследуемые формы его окружали, и слой сохранения данных обычно не является тем местом, где вы хотите много наследования.
Не все, что важно в классе, легко утверждается в коде. Здесь могут присутствовать семантика и отношения, которые легко нарушаются при наследовании и переопределении методов. Переопределение одного метода за один раз-это простой способ сделать это. Вы проектируете класс / объект как единую значимую сущность, а затем кто-то приходит и думает, что если бы метод или два были 'better', это не принесло бы никакого вреда. Это может быть правдой, а может и нет. Возможно, вы можете правильно разделить все методы между частными и не частными или виртуальными и не виртуальными, но этого все равно может быть недостаточно. Требование наследования всех классов также накладывает огромную дополнительную нагрузку на первоначального разработчика, чтобы предвидеть все способы, которыми наследующий класс может все испортить.
Я не знаю идеального решения. Я сочувствую предотвращению наследования, но это также проблема, потому что это мешает модульному тестированию.
Я представил минимальный интерфейс для взаимодействия с клиентом API, и было бы здорово расширить класс client API, а затем просто добавить предложение implements с моим новым интерфейсом. Методы, которые я имел в интерфейсе, который соответствовал фактическому интерфейсу, тогда не нуждались бы в дополнительных деталях, и поэтому мне не пришлось бы явно реализовывать их. Тем не менее, класс был запечатан, поэтому мне пришлось вместо прокси вызывать внутреннюю ссылку на этот класс. Результат: больше работы и намного больше кода без какой-либо реальной причины.
Я представил минимальный интерфейс для взаимодействия с клиентом API, и было бы здорово расширить класс client API, а затем просто добавить предложение implements с моим новым интерфейсом. Методы, которые я имел в интерфейсе, который соответствовал фактическому интерфейсу, тогда не нуждались бы в дополнительных деталях, и поэтому мне не пришлось бы явно реализовывать их. Тем не менее, класс был запечатан, поэтому мне пришлось вместо прокси вызывать внутреннюю ссылку на этот класс. Результат: больше работы и намного больше кода без какой-либо реальной причины.
Ну, есть причина: ваш код теперь несколько изолирован от изменений в интерфейсе memcached.
Производительность: ( ... ) если компилятор JIT видит вызов виртуального метода с использованием запечатанных типов, компилятор JIT может создать более эффективный код, вызвав метод не виртуально.(…)
Производительность: ( ... ) если компилятор JIT видит вызов виртуального метода с использованием запечатанных типов, компилятор JIT может создать более эффективный код, вызвав метод не виртуально.(…)
Это действительно великая причина. Таким образом, для критичных к производительности классов, sealed и друзья имеют смысл.
Все остальные причины, которые я видел до сих пор, сводятся к "nobody touches my class!". Если вы боитесь, что кто-то может неправильно понять его внутренние органы, вы плохо документировали его. Вы не можете знать, что нет ничего полезного, чтобы добавить к вашему классу, или что вы уже знаете все мыслимые варианты его использования. Даже если вы правы и другой разработчик не должен был использовать ваш класс для решения своей проблемы, использование ключевого слова-не лучший способ предотвратить такую ошибку. Документация есть. Если они проигнорируют документацию, то их потеря.