Наследование действительно необходимо?

Добро пожаловать в StackOverflow и поздравляем с первым вопросом!

Мне кажется, ваш вопрос заслуживает двух ответов:

  • Как правильно использовать многопоточность.
  • Один из способов ускорения суммирования целых чисел от 1 до N.

Как использовать многопоточность

В многопоточности всегда есть некоторые накладные расходы: создание потоков, блокировка и т. Д. Даже если эти накладные расходы действительно малы, некоторые накладные расходы останутся. Это означает, что многопоточность того стоит, только если у каждого потока есть существенная работа, которую нужно выполнить. Ваша функция почти ничего не делает внутри lock.acquire()/release(). Потоки сработали бы, если бы вы выполняли там более сложную работу. Но вы используете функцию блокировки правильно - это не очень хорошая проблема для многопоточности.

Быстрое суммирование 1..N

Измерение, измерение, измерение

Я использую Python 3.7, и у меня установлено pytest-benchmark 3.2.2. Pytest-benchmark запускает функцию для вас соответствующее количество раз и сообщает статистику по времени выполнения.

import threading
import pytest


def compute_variant1(N: int) -> int:
    global sum_
    global i
    sum_ = 0
    i = 1
    lock = threading.Lock()
    def thread_worker():
        global sum_
        global i
        lock.acquire()
        sum_ += i
        i += 1
        lock.release()

    threads = []
    for j in range(N):
        threads.append(threading.Thread(target = thread_worker))
        threads[-1].start()
    for t in threads:
        t.join()

    return sum_

@pytest.mark.parametrize("func", [
    compute_variant1])
@pytest.mark.parametrize("N,expected", [(10, 55), (30, 465), (100, 5050)])
def test_var1(benchmark, func, N, expected):
    result = benchmark(func, N=N )
    assert result == expected

Запустите это с: py.test --benchmark-histogram=bench И откройте сгенерированный bench.svg Он также печатает эту таблицу:

----------------------------------------------------------------------------------------------------- benchmark: 3 tests -----------------------------------------------------------------------------------------------------
Name (time in us)                               Min                    Max                  Mean                StdDev                Median                 IQR            Outliers         OPS            Rounds  Iterations
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
test_var1[10-55-compute_variant1]          570.0710 (1.0)       4,900.3160 (1.51)       658.8392 (1.0)        270.6056 (1.36)       602.5220 (1.0)       42.6373 (1.0)         19;82  1,517.8209 (1.0)         529           1
test_var1[30-465-compute_variant1]       1,701.2970 (2.98)      3,237.5830 (1.0)      1,879.8490 (2.85)       198.3905 (1.0)      1,802.5160 (2.99)     146.4465 (3.43)        59;43    531.9576 (0.35)        432           1
test_var1[100-5050-compute_variant1]     5,809.7500 (10.19)    13,354.3520 (4.12)     6,698.4778 (10.17)    1,413.1428 (7.12)     6,235.4355 (10.35)    766.0440 (17.97)         6;7    149.2876 (0.10)         74           1
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Упрощенные алгоритмы быстрее

Часто это действительно полезно сравнить быструю реализацию с самой простой.

Самая простая реализация, о которой я могу подумать - без использования трюков Python - это простой цикл:

def compute_variant2(N):
    sum_ = 0
    for i in range(1, N+1):
        sum_ += i
    return sum_

В Python есть функция с именем sum(), которая принимает список или любой итеративный как range(N):

def compute_variant3(N: int) -> int:
    return sum(range(1, N+1))

Вот результаты (я удалил некоторые столбцы):

------------------------------------------------------- benchmark: 3 tests ------------------------------------------------
Name (time in us)                             Mean              StdDev                Median             Rounds  Iterations
---------------------------------------------------------------------------------------------------------------------------
test_var1[100-5050-compute_variant2]        4.2328 (1.0)        1.6411 (1.0)          4.1150 (1.0)       163106           1
test_var1[100-5050-compute_variant3]        4.7113 (1.11)       1.6773 (1.02)         4.5560 (1.11)      141744           1
test_var1[100-5050-compute_variant1]    6,404.1856 (>1000.0)  668.2502 (407.21)   6,257.6385 (>1000.0)      106           1
---------------------------------------------------------------------------------------------------------------------------

Как вы можете видеть variant1 на основе потоков намного, во много раз медленнее, чем последовательные реализации.

Еще быстрее, используя математику

Карл Фридрих Гаусс, когда он был ребенком 1 , заново открыл решение нашей проблемы в форме аклозы: N * (N + 1) / 2.

Это можно выразить в Python:

def compute_variant4(N: int) -> int:
    return N * (N + 1) // 2

Давайте сравним это с другими быстрыми реализациями (я опущу первую реализацию):

Benchmark Results

И, как вы можете видеть из таблицы: последний вариант быстрее, и, что важно, не зависит от N .

---------------------------------------------------------------------------------------------------- benchmark: 12 tests -----------------------------------------------------------------------------------------------------
Name (time in ns)                          Min                     Max                  Mean                StdDev                Median                 IQR              Outliers  OPS (Kops/s)            Rounds  Iterations
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
var1[10-55-compute_variant4]          162.0100 (1.0)        1,053.6800 (1.0)        170.1799 (1.0)         32.5862 (1.0)        163.7200 (1.0)        8.1800 (1.59)      1135;1225    5,876.1353 (1.0)       58310         100
var1[13-91-compute_variant4]          162.1400 (1.00)       1,354.3200 (1.29)       181.5037 (1.07)        46.2132 (1.42)       176.1800 (1.08)       5.1500 (1.0)      2214;14374    5,509.5296 (0.94)      61452         100
var1[30-465-compute_variant4]         188.8900 (1.17)       1,874.2800 (1.78)       200.4983 (1.18)        40.8533 (1.25)       191.0000 (1.17)       9.8300 (1.91)      1245;1342    4,987.5732 (0.85)      51919         100
var1[100-5050-compute_variant4]       192.9091 (1.19)       5,938.7273 (5.64)       209.0628 (1.23)        75.8845 (2.33)       198.5455 (1.21)      10.9091 (2.12)      1879;4696    4,783.2508 (0.81)     194515          22
var1[10-55-compute_variant2]          676.1000 (4.17)      18,987.8000 (18.02)      719.4231 (4.23)       194.5556 (5.97)       689.0000 (4.21)      34.9000 (6.78)      1447;2199    1,390.0027 (0.24)     125898          10
var1[13-91-compute_variant2]          753.9000 (4.65)      12,103.8000 (11.49)      799.2837 (4.70)       201.3654 (6.18)       766.5000 (4.68)      38.1000 (7.40)      1554;3049    1,251.1203 (0.21)     124441          10
var1[10-55-compute_variant3]        1,021.0000 (6.30)      77,718.8000 (73.76)    1,157.6125 (6.80)       544.1982 (16.70)    1,098.2000 (6.71)      73.0000 (14.17)    3802;12244      863.8469 (0.15)     186672           5
var1[13-91-compute_variant3]        1,127.6000 (6.96)      44,606.4000 (42.33)    1,279.9332 (7.52)       476.7700 (14.63)    1,200.2000 (7.33)      90.0000 (17.48)    4022;21018      781.2908 (0.13)     172147           5
var1[30-465-compute_variant2]       1,304.7500 (8.05)      48,218.5000 (45.76)    1,457.3923 (8.56)       550.7975 (16.90)    1,385.7500 (8.46)      80.2500 (15.58)    3944;22221      686.1570 (0.12)     177086           4
var1[30-465-compute_variant3]       1,738.6667 (10.73)     86,587.3333 (82.18)    1,935.1659 (11.37)      762.7118 (23.41)    1,860.3333 (11.36)    128.6667 (24.98)     2474;6870      516.7516 (0.09)     176773           3
var1[100-5050-compute_variant2]     3,891.0000 (24.02)    181,009.0000 (171.79)   4,218.4608 (24.79)    1,721.8413 (52.84)    3,998.0000 (24.42)    210.0000 (40.78)     1788;2670      237.0533 (0.04)     171881           1
var1[100-5050-compute_variant3]     4,200.0000 (25.92)     76,885.0000 (72.97)    4,516.5853 (26.54)    1,452.5713 (44.58)    4,343.0000 (26.53)    210.0000 (40.78)     1204;2311      221.4062 (0.04)     153587           1
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

32
задан Alan 10 November 2008 в 17:15
поделиться

20 ответов

Действительно действительно короткий ответ: Нет. Наследование не нужно, потому что только код байта действительно необходим. Но очевидно, код байта или собираются, не практически способ записать Вашу программу. ООП не является единственной парадигмой для программирования. Но, я отступаю.

я поступил в институт для информатики в начале 2000-х, когда наследование (a), составы (имеет a) и интерфейсы (делает a) преподавалось равным условиям. Из-за этого я использую очень мало наследования, потому что оно часто удовлетворяется лучше составом. Это было подчеркнуто, потому что многие преподаватели видели плохой код (наряду с тем, что Вы описали) из-за злоупотребления наследованием.

Независимо от создания языка с или без наследований, можно ли создать язык программирования, который предотвращает дурные привычки и плохие проектные решения?

33
ответ дан 27 November 2019 в 19:44
поделиться

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

7
ответ дан 27 November 2019 в 19:44
поделиться

Оборотные стороны состава - то, что он может маскировать связанность элементов, и он может быть более трудным для других понять. С, скажем, 2D классом Точки и требованием расширить его до более высоких размеров, необходимо было бы, по-видимому, добавить (по крайней мере), Z метод считывания/метод set, изменить getDistance () и возможно добавить getVolume () метод. Таким образом, у Вас есть Объекты 101 элемент: связанное состояние и поведение.

разработчик А с композиционным мышлением, по-видимому, определил бы getDistance (x, y)-> двойной метод и теперь определит getDistance (x, y, z)-> двойной метод. Или, думая обычно, они могли бы определить getDistance (lambdaGeneratingACoordinateForEveryAxis ())-> двойной метод. Затем они, вероятно, записали бы createTwoDimensionalPoint () и createThreeDimensionalPoint () методы фабрики (или возможно createNDimensionalPoint (n)), который сошьет вместе различное состояние и поведение.

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

Теперь думают об этом от понятности / точка зрения удобочитаемости. Для понимания состава у каждого есть большое количество функций, которые составлены программно в другой функции. Таким образом, существует мало с точки зрения статического кода 'структура' (файлы и ключевые слова и т.д), который делает связанность Z, и расстояние () выскакивают . В мире OO у Вас есть большой большой красный сигнальный огонь, говоря Вам иерархию. Кроме того, у Вас есть чрезвычайно универсальный словарь для обсуждения структуры, широко известных графических нотаций, естественная иерархия (по крайней мере, для единичного наследования), и т.д.

Теперь, с другой стороны, хорошо названный и созданный Метод фабрики будет часто делать явным больше иногда неясных отношений между состоянием и поведением, так как композиционное мышление упрощает функциональный код (то есть, кодируйте, который передает состояние через параметры, не через [1 110] это ).

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

3
ответ дан 27 November 2019 в 19:44
поделиться

Наследование является решением реализации. Интерфейсы почти всегда представляют лучший дизайн и должны обычно использоваться во внешнем API.

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

Этот ответ к другому вопросу суммирует мои взгляды вполне прилично.

4
ответ дан 27 November 2019 в 19:44
поделиться

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

Весь бизнес заботится о, производит (качество, конечно) дешево и быстро .

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

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

3
ответ дан 27 November 2019 в 19:44
поделиться

Проблема с наследованием состоит в том, что оно объединяет проблему выделения подтипов (утверждение - отношения) и повторное использование кода (например, частное наследование только для повторного использования).

Так, нет это - перегруженное слово, в котором мы не нуждаемся. Я предпочел бы выделять подтипы (использование ключевого слова 'реализаций'), и импорт (своего рода как Ruby делает это в определениях классов)

11
ответ дан 27 November 2019 в 19:44
поделиться

Кто-либо еще помнит всех пуристов OO, идущих баллистический по реализации COM "включения" вместо "наследования?" Это достигло по существу того же самого, но с другим видом реализации. Это напоминает мне о Вашем вопросе.

я строго стараюсь избегать религиозных войн в разработке программного обеспечения. ("vi" ИЛИ "emacs"..., когда все знают его "vi"!) Я думаю, что они - знак маленьких умов. Аккомпанемент Научные профессора может позволить себе сидеть без дела и обсудить эти вещи. Я работаю в реальном мире и мог заботиться меньше. Весь этот материал является просто попытками предоставления полезных решений настоящих проблем. Если они будут работать, то люди будут использовать их. То, что языки OO и инструменты были коммерчески доступны в широком масштабе для продолжения на 20 лет, является довольно хорошей ставкой, что они полезны для большого количества людей.

3
ответ дан 27 November 2019 в 19:44
поделиться

Я думаю, прося ситуации, где наследование действительно необходимо, упускает суть немного. Можно фальсифицировать наследование при помощи интерфейса и некоторого состава. Это не означает, что наследование бесполезно. Можно сделать что-либо, что Вы сделали в VB6 в ассемблерном коде с некоторым дополнительным вводом, который не означает, что VB6 был бесполезен.

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

22
ответ дан 27 November 2019 в 19:44
поделиться

GoF (и многие другие) рекомендуют что Вы только польза состав по наследованию. Если у Вас есть класс с очень большим API, и Вы только хотите добавить очень небольшое количество методов к нему, оставляя базовое внедрение в покое, я нашел бы несоответствующим использовать состав. Необходимо было бы повторно реализовать все открытые методы инкапсулированного класса просто возвратить их значение. Это - пустая трата времени (программист и ЦП), когда можно просто наследовать все это поведение и провести время, концентрируясь на новых методах.

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

12
ответ дан 27 November 2019 в 19:44
поделиться

Наследование определяет отношения "ISA".

class Point( object ):
    # some set of features: attributes, methods, etc.

class PointWithMass( Point ):
    # An additional feature: mass.

Выше, я использовал наследование, чтобы официально объявить, что PointWithMass является Точка.

существует несколько способов обработать объект P1 являющийся PointWithMass, а также Точкой. Вот два.

  1. Имеют ссылку от PointWithMass объект p1 к некоторому Точечному объекту p1-friend. Эти p1-friend имеет Point атрибуты. Когда p1 потребности участвовать в Point - как поведение, это должно делегировать работу своему другу.

  2. Полагаются на наследование языка, чтобы гарантировать, что все функции Point также применимы к моему PointWithMass объект, p1. Когда p1 потребности уже участвовать в [1 113] - как поведение, это Point объект и может просто сделать что потребности быть сделанным.

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

Редактирование.

Для статически типизированных языков, существует премия. То, когда я полагаюсь на язык для обработки, это, PointWithMass может использоваться где угодно Point, ожидалось.

Для действительно неясного злоупотребления наследованием, считайте о странном "составе C++ посредством частного наследования" болото. См. Какие-либо разумные примеры создания наследования, не создавая выделение подтипов в отношениях? для некоторого дальнейшего обсуждения этого. Это объединяет наследование и состав; это, кажется, не добавляет ясность или точность к получающемуся коду; это только относится к C++.

14
ответ дан 27 November 2019 в 19:44
поделиться

В следующем наследование используется для представления особого свойства для всех нескольких определенных воплощений той же вещи типа. В этом случае GeneralPresenation имеет свойства, которые относятся ко всей "презентации" (данные передали представлению MVC). Ведущее устройство Page является единственной вещью с помощью него и ожидает GeneralPresentation, хотя определенные представления ожидают больше информации, адаптированной в соответствии с их потребностями.

   public abstract class GeneralPresentation
    {
        public GeneralPresentation()
        {
            MenuPages = new List<Page>();
        }
        public IEnumerable<Page> MenuPages { get; set; }
        public string Title { get; set; }
    }

    public class IndexPresentation : GeneralPresentation
    {
        public IndexPresentation() { IndexPage = new Page(); }
        public Page IndexPage { get; set; }
    }

    public class InsertPresentation : GeneralPresentation
    {
        public InsertPresentation() { 
          InsertPage = new Page(); 
          ValidationInfo = new PageValidationInfo(); 
        }
        public PageValidationInfo ValidationInfo { get; set; }
        public Page InsertPage { get; set; }
    }
0
ответ дан 27 November 2019 в 19:44
поделиться

Благодаря всем для Ваших ответов. Я сохраняю свою позицию, что строго говоря наследование не необходимо, хотя я полагаю, что нашел новую оценку для этой функции.

Что-то еще: В моем опыте задания я нашел, что наследование приводит к более простым, более ясным проектам, когда это вводится поздно в проекте, после того, как это заметило, что много классов имеет много общности, и Вы создаете базовый класс. В проектах, где главная схема была создана с самого начала с большим количеством классов в иерархии наследования, рефакторинг является обычно болезненным и трудным.

Наблюдение некоторых ответов, упоминая что-то подобное заставляет меня задаться вопросом, не могло ли это быть точно, как наследование предположило, чтобы использоваться: действие с обратной силой. Напоминает мне о кавычке Stepanov: "Вы не запускаете с аксиом, Вы заканчиваете с аксиомами после того, как у Вас есть набор связанных доказательств". Он - математик, таким образом, он должен знать что-то.

1
ответ дан 27 November 2019 в 19:44
поделиться

Абсолютно необходимый? нет, Но думайте о лампах. Можно создать новую лампу с нуля каждый раз, когда Вы делаете один, или можно взять свойства от исходной лампы и сделать все виды новых стилей лампы, которые имеют те же свойства как оригинал, каждый с их собственным стилем.

Или можно сделать новую лампу с нуля или сказать людям смотреть на нее определенный способ видеть свет, или, или, или

Не требуемый, но хороший :)

1
ответ дан 27 November 2019 в 19:44
поделиться

Я соглашаюсь со всеми остальными о необходимом/полезном различии.

причина мне нравится ООП, то, потому что это позволяет мне написать код, это более чисто и более логически организовано. Одно из самых больших преимуществ прибывает от способности до "факторной" логики, это характерно для многих классов. Я мог дать Вам конкретные примеры, где ООП серьезно уменьшило сложность моего кода, но это было бы скучно для Вас.

Достаточно сказать, я сердечное ООП.

1
ответ дан 27 November 2019 в 19:44
поделиться

Наследование является хорошей вещью, когда подкласс действительно является тем же видом объекта как суперкласс. Например, если Вы реализуете Активный Рекордный шаблон, Вы пытаетесь отобразить класс на таблицу в базе данных и экземпляры класса к строке в базе данных. Следовательно, очень вероятно, что Ваши Активные Рекордные классы совместно используют единый интерфейс и реализацию методов как: что является первичным ключом, сохраняется ли текущий экземпляр, сохраняя текущий экземпляр, проверяя текущий экземпляр, выполняя обратные вызовы после проверки и/или сохранения, удаления текущего экземпляра, выполнения SQL-запроса, возврата названия таблицы, на которую отображается класс, и т.д.

также кажется от того, как Вы формулируете свой вопрос, что Вы предполагаете, что наследование является единичным, но не несколько. Если нам нужно множественное наследование, то мы должны использовать интерфейсы плюс состав для осуществления задания. Для помещения тонкости об этом Java предполагает, что наследование реализации исключительно, и интерфейсное наследование может быть несколькими. Один не должен идти этим путем. Например, C++ и Ruby разрешают множественное наследование для Вашей реализации и Вашего интерфейса. Тем не менее нужно использовать множественное наследование с осторожностью (т.е. сохранить Ваши абстрактные классы виртуальными и/или не сохраняющими состояние).

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

1
ответ дан 27 November 2019 в 19:44
поделиться

Нет.

для меня, ООП главным образом об инкапсуляции состояния и поведения и полиморфизма.

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

, но существуют другие пути, я думаю, что самым общим был бы утиный ввод и тесно связанное, основанное на прототипе ООП (который не является наследованием на самом деле, но это обычно называло основанное на прототипе наследование).

2
ответ дан 27 November 2019 в 19:44
поделиться

Не нужный, но полезный.

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

1
ответ дан 27 November 2019 в 19:44
поделиться

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

, Но существуют определенно случаи, где наследование полезно. Как Вы говорите, состав плюс интерфейсы вместе покрывают почти все случаи, но что, если я хочу предоставить поведение по умолчанию? Интерфейс не может сделать этого. Базовый класс может. Иногда, то, что Вы хотите сделать, действительно просто переопределить отдельные методы. Не повторно реализуют класс с нуля (как с интерфейсом), но просто изменяют один аспект его. или Вы не можете хотеть, чтобы все члены класса были переопределяемыми. Возможно, у Вас есть только один или два членских метода, которые Вы хотите, чтобы пользователь переопределил, и остальные, которые вызовы они (и выполняет проверку и другие важные задачи прежде и после пользовательских переопределенных методов) указаны раз и навсегда в базовом классе и не могут быть переопределены.

Наследование часто используется в качестве опоры людьми, которые слишком одержимы узким определением Java (и одержимость) ООП, хотя, и в большинстве случаев я соглашаюсь, это - неверное решение, как будто чем глубже Ваша иерархия классов, тем лучше Ваше программное обеспечение.

2
ответ дан 27 November 2019 в 19:44
поделиться

Существует много полезных использований наследования, и вероятно так же, как многие, которые менее полезны. Один из полезных является потоковым классом.

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

2
ответ дан 27 November 2019 в 19:44
поделиться

Самая большая проблема с интерфейсами в том, что они не могут быть изменены. Сделайте интерфейс публичным, затем измените его (добавьте к нему новый метод) и сломайте миллионы приложений по всему миру, потому что они реализовали ваш интерфейс, но не новый метод. Приложение может даже не запуститься, ВМ может отказаться его загружать.

Использовать базовый класс (не абстрактный) другие программисты могут наследовать (и переопределять методы по мере необходимости); затем добавить в него метод. Каждое приложение, использующее ваш класс, все равно будет работать, этот метод просто никто не будет переопределять, но так как вы предоставляете базовую реализацию, то этот метод будет использоваться, и он может отлично работать для всех подклассов вашего класса... это также может привести к странному поведению, так как иногда переопределение было бы необходимо, ладно, может быть так, но, по крайней мере, все эти миллионы приложений в мире все равно запустятся!

Я предпочитаю, чтобы мое Java-приложение все еще работало после обновления JDK с 1.6 до 1.7 с некоторыми незначительными ошибками (которые могут быть исправлены с течением времени), чем не запускать его вообще (заставляя немедленно исправлять или это будет бесполезно для людей).

.
1
ответ дан 27 November 2019 в 19:44
поделиться