Рассмотрим интерфейс:
interface IWaveGenerator
{
SoundWave GenerateWave(double frequency, double lengthInSeconds);
}
Этот интерфейс реализован рядом классов, которые генерируют волны различной формы (например, SineWaveGenerator
и SquareWaveGenerator
).
Я хочу реализовать класс, который генерирует SoundWave
музыкальные данные, а не необработанные звуковые данные. Он получит имя заметки и длину в виде ударов (не секунд) и внутренне использует IWaveGenerator
функциональные возможности для создания SoundWave
соответствующего.
Вопрос в том, должен ли он NoteGenerator
содержать IWaveGenerator
или должен наследоваться от IWaveGenerator
реализации?
Я склоняюсь к композиции по двум причинам:
1- Это позволяет мне вводить любого IWaveGenerator
к NoteGenerator
динамически. Кроме того , мне нужен только один NoteGenerator
класс, вместо SineNoteGenerator
, SquareNoteGenerator
и т.д.
2. Нет необходимости NoteGenerator
выставлять интерфейс нижнего уровня, определенный IWaveGenerator
.
Однако я публикую этот вопрос, чтобы услышать другие мнения по этому поводу, может быть, пункты, о которых я не задумывался.
Кстати, я бы сказал NoteGenerator
, концептуально, IWaveGenerator
потому что он генерирует SoundWave
s.
Является ли NoteGenerator «концептуально» IWaveGenerator или нет, не имеет значения.
Вы должны наследовать от интерфейса, только если планируете реализовать этот точный интерфейс в соответствии с принципом подстановки Лискова, то есть с правильной семантикой и правильным синтаксисом.
Похоже, ваш NoteGenerator может иметь синтаксически тот же интерфейс, но его семантика (в данном случае значения параметров, которые он принимает) будет сильно отличаться, поэтому использование наследования в этом случае будет очень вводящим в заблуждение и потенциально подверженным ошибкам. Вы правы, предпочитая композицию здесь.
источник
NoteGenerator
что реализовывал бы,GenerateWave
а интерпретировал параметры по-другому, да, я согласен, что это была бы ужасная идея. Я имел в виду, что NoteGenerator является своего рода специализацией волнового генератора: он может принимать входные данные «более высокого уровня», а не только необработанные звуковые данные (например, имя ноты вместо частоты). То естьsineWaveGenerator.generate(440) == noteGenerator.generate("a4")
. Таким образом, возникает вопрос, состав или наследство.Похоже,
NoteGenerator
это неWaveGenerator
так, поэтому не следует реализовывать интерфейс.Композиция - это правильный выбор.
источник
NoteGenerator
, концептуально,IWaveGenerator
потому что он генерируетSoundWave
с.GenerateWave
, то это не такIWaveGenerator
. Но, похоже, он использует IWaveGenerator (может, больше?), Следовательно, композицию.GenerateWave
функции, как написано в вопросе. Но из комментария выше я думаю, что это не то, что на самом деле имел в виду ФП.У вас есть веские аргументы в пользу композиции. У вас может быть дело также добавить наследство. Способ сказать, посмотрев на код вызова. Если вы хотите иметь возможность использовать
NoteGenerator
в существующем коде вызова, который ожидаетIWaveGenerator
, то вам нужно реализовать интерфейс. Вы ищете потребность в заменяемости. Является ли это концептуальным генератором волн "это что-то", не имеет значения.источник
IHasWaveGenerator
, «наследование» , и соответствующий метод на этом интерфейсе будетGetWaveGenerator
возвращать экземплярIWaveGenerator
. Конечно, название можно изменить. (Я просто пытаюсьЭто хорошо для
NoteGenerator
реализации интерфейса, а также для того,NoteGenerator
чтобы иметь внутреннюю реализацию, которая ссылается (по составу) на другуюIWaveGenerator
.Как правило, компоновка приводит к более поддерживаемому (то есть читаемому) коду, потому что у вас нет сложностей переопределений для пересмотра. Ваше наблюдение за матрицей классов, которую вы имели бы при использовании наследования, также является точным и, вероятно, может восприниматься как запах кода, указывающий на композицию.
Наследование лучше использовать, когда у вас есть реализация, которую вы хотите специализировать или настроить, что, похоже, не так: вам просто нужно использовать интерфейс.
источник
NoteGenerator
реализации,IWaveGenerator
потому что примечания требуют ударов. не секунды.NoteGenerator
, концептуально,IWaveGenerator
потому что он генерируетSoundWave
s», и он рассматривал наследование, поэтому я взял умственную широту для возможности, что может быть некоторая реализация интерфейса, даже если есть другая лучший интерфейс или подпись для класса.