Язык не имеет значения при работе с композицией против наследования. Если вы понимаете, что такое класс и что такое экземпляр класса, то у вас есть все, что вам нужно.
Композиция - это просто, когда класс состоит из других классов; или, говоря иначе, экземпляр объекта имеет ссылки на экземпляры других объектов.
Наследование - это когда класс наследует методы и свойства от другого класса.
Допустим, у вас есть две функциональности, A и B. Вы хотите определить третью функциональность, C, которая имеет некоторые или все как A, так и B. Вы можете сделать C расширенным от B и A, и в этом случае C имеет все, что есть у B и A, потому что C isA
B и A, или вы можете сделать так, чтобы каждый экземпляр C имел экземпляр A и экземпляр B, и вызывал элементы с этими функциями. В последнем случае каждый экземпляр C фактически оборачивает экземпляр B и экземпляр A.
Конечно, в зависимости от языка, вы не сможете расширить класс из 2 классов (например, Java не поддерживает множественное наследование), но это специфичная для языка деталь, которая не имеет ничего общего с концепцией.
Теперь, для деталей по конкретному языку ...
Я использовал слово class , но javascript не имеет понятия Class как таковой. У него есть объекты, и это все (кроме простых типов). Javascript использует прототип наследования, что означает, что он имеет способ эффективного определения объектов и методов для этих объектов (это тема для другого вопроса; вы можете искать SO, так как уже есть ответы.)
Итак, в нашем примере выше у вас есть A, B и C.
Для наследования у вас будет
// define an object (which can be viewed as a "class")
function A(){}
// define some functionality
A.prototype.someMethod = function(){}
Если бы вы хотели, чтобы C расширил A, вы бы сделали
C.prototype = new A();
C.prototype.constructor = A;
Теперь каждый экземпляр C будет иметь метод someMethod
, потому что каждый экземпляр C "isA" A.
Javascript не имеет множественного наследования * (подробнее об этом позже), поэтому вы не можете использовать C для расширения как A, так и B. Однако вы можете использовать композицию, чтобы придать ей функциональность. На самом деле, это одна из причин, по которой состав предпочитают некоторые по наследству; нет ограничений на объединение функций (но это не единственная причина).
function C(){
this.a = new A();
this.b = new B();
}
// someMethod on C invokes the someMethod on B.
C.someMethod = function(){
this.a.someMethod()
}
Итак, есть ваши простые примеры как наследования, так и композиции. Тем не менее, это не конец истории. Я уже говорил, что Javascript не поддерживает множественное наследование, и в некотором смысле это не так, потому что вы не можете основать прототип объекта на прототипах нескольких объектов; то есть вы не можете сделать
C.prototype = new B();
C.prototype.constructor = B;
C.prototype.constructor = A;
потому что, как только вы делаете третью строку, вы просто отменяете вторую строку. Это имеет значение для оператора instanceof
.
Однако это на самом деле не имеет значения, потому что только потому, что вы не можете переопределить конструктор объекта дважды, вы все равно можете добавить любые методы, которые вы хотите, к прототипу объекта . Так что, поскольку вы не можете выполнить приведенный выше пример, , вы все равно можете добавить в C.prototype все, что захотите, включая все методы прототипов A и B.
Многие фреймворки поддерживают это и делают его легким. Я много работаю в Sproutcore; с этой структурой вы можете сделать
A = {
method1: function(){}
}
B = {
method2: function(){}
}
C = SC.Object.extend(A, B, {
method3: function(){}
}
Здесь я определил функциональность в объектных литералах A
и B
, а затем добавил функциональность обоих к C
, поэтому каждый экземпляр C имеет методы 1, 2 и 3. В этом конкретном случае Метод extend
(предоставляется каркасом) выполняет всю тяжелую работу по настройке прототипов объектов.
РЕДАКТИРОВАТЬ - В ваших комментариях вы задаете хороший вопрос, а именно: «Если вы используете композицию, как вы согласовываете область действия основного объекта с областью объектов, из которых состоит основной объект».
Есть множество способов. Первый - просто передать аргументы. Так
C.someMethod = function(){
this.a.someMethod(arg1, arg2...);
}
Здесь вы не возитесь с областями видимости, вы просто передаете аргументы.Это простой и очень жизнеспособный подход.(аргументы могут быть взяты из this
или переданы, как угодно ...)
Другой способ сделать это - использовать call
(или apply
) методы javascript, которые в основномпозволяет вам установить область действия функции.
C.someMethod = function(){
this.a.someMethod.call(this, arg1, arg2...);
}
, чтобы быть немного более понятным, следующее эквивалентно
C.someMethod = function(){
var someMethodOnA = this.a.someMethod;
someMethodOnA.call(this, arg1, arg2...);
}
В javascript функции являются объектами, поэтому вы можете назначитьих переменным.
вызов call
здесь устанавливает область действия someMethodOnA
в this
, который является экземпляром C.