Просто о композиции, агрегации и ассоциации в JavaScript

Spread the love

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

Композиция, агрегация и ассоциация — все эти термины или точнее парадигмы ООП про отношения между объектами или классами между собой. Всего существует пять основных типов отношений:
Ассоциация
Агрегарция
Композиция
и еще два типа, которые в этой статье рассматривать не будем:
Наследования (иногда этот тип еще называют генерализацией)
Реализация (это тип отношений базируется на интерфейсах. То есть создается интерфейсы, которые основной класс должен реализовать.)

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

Самый простой способ понять эти термины это использовать аналогию из реального мира. Представим себе что у нас есть класс комната и есть два других класса мебель и стена. Мы можем сказать что у комнаты будет какая та мебель и какие то стены. То есть объект комната может использоваться объекты стены и мебель по мере необходимости. Но есть разница в отношения комната — стены и комната — мебель. Разница в том что стены никогда не выйдут из объекта комната. Стены не могут существовать вне комнаты. То есть стена всегда будет создаваться внутри объекта комната. Такая связь называется композиция. И эта связь будет жесткой. Зато мебель очень легко представить за пределами комнаты. Один экземпляр мебели может принадлежать с начало одной комнате потом другой. Такая связь называется ассоциацией или агрегацией. И такая связь будет более гибкой. О различие между ассоциацией или агрегацией чуть позже.

Способ реализации отношений между классами или объектами и есть основное концептуальное различие между композицией, агрегацией и ассоциацией . При композиции мы не можем что то вынести а при агрегации или ассоциации мы можем передать объект из одного объекта в другой.

Рассмотрим эти связи подробнее.

Ассоциация

Ассоциация это такой тип при котором объекты будут ссылаться друг на друга. При этом они остаются полностью независимыми друг от друга.

Пример реализации ассоциации

class Logger {
  constructor() {
    this.stream = null;
  }
  log(message) {
    if (this.stream) {
      this.stream.write(message);
    }
  }
}

const logger = new Logger();
logger.stream = process.stdout;
logger.log('Here we are');

Тут у нас есть атрибут stream, в котором должна быть ссылка на поток который куда то будет отправлять сообщение. Метод log проверяет что в классе задан stream и использует его для отправки сообщения. То есть ассоциация это когда у одного класса есть ссылка на другой класс. Это создает большую гибкость в отношениях между классами. Например у нас есть возможность смены потока во время выполнения программы.

Агрегация

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

Пример реализации агрегации:

class Logger {
  constructor(stream) {
    this.stream = stream;
  }
  log(message) {
    if (this.stream) {
      this.stream.write(message);
    }
  }
}

const logger = new Logger(process.stdout);
logger.log('Here we are');

При агрегации у нас получается более прочная связь, чем при ассоциации. В данном случае связь образуется при создание экземпляра класса Logger, в конструктор которого мы передаем другой класс.

Композиция

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

Пример реализации композиции

const fs = require('fs')

class Logger {
  constructor(name) {
    this.stream = fs.createWriteStream(name);
  }
  log(message) {
    this.stream.write(message);
  }
}

const logger = new Logger('file.log');
logger.log('Here we are');

В данном случае внутри конструктора создается экземпляр другого класса. При этом создается более крепкая связанность этих двух классов. То есть класс Logger обязательно знает что существует библиотека fs, у него есть класс fs и у него есть метод createWriteStream и т. д. Он полностью знает реализацию другого класса.

То есть в ассоциации и агрегации предполагалось что эти два класса создается независимо, а потом связываются ссылками, в композиции они создается вместе в момент вызова конструктора.

Источники для статьи:
Ассоциация, агрегация и композиция объектов в JavaScript
Агрегация и Композиция

Практические вопросы на проверку понимания

1. Какой тип отношений?


class Salary {
	constructor(pay, bonus) {
		this.pay = pay;
		this.bonus = bonus;
	}

	annual_salary() {
		return (this.pay * 12) + this.bonus;
	}
}

class Employee {
	constructor(name, age, salary) {
		this.name = name;
		this.age = age;
		this.salary = salary;
	}

	total_salary() {
		if (this.salary) {
			return this.salary.annual_salary();
		}
	}
}

let salary = new Salary(15000, 10000)
let emp = Employee('Max', 25, salary)

console.log(emp.total_salary())
 
 
 

2. Какой тип отношений?

class Salary {
	constructor(pay, bonus) {
		this.pay = pay;
		this.bonus = bonus;
	}

	annual_salary() {
		return (this.pay * 12) + this.bonus;
	}
}

class Employee {
	constructor(name, age, pay, bonus) {
		this.name = name;
		this.age = age;
		this.salary = new Salary(pay, bonus);
	}

	total_salary() {
		return this.salary.annual_salary();
	}
}


let emp = Employee('Max', 25, 15000, 10000)
console.log(emp.total_salary())


 
 
 

3. Какой тип отношений?


class Salary {
	constructor(pay, bonus) {
		this.pay = pay;
		this.bonus = bonus;
	}

	annual_salary() {
		return (this.pay * 12) + this.bonus;
	}
}

class Employee {
	constructor(name, age) {
		this.name = name;
		this.age = age;
		this.salary = null;
	}

	total_salary() {
		if (this.salary) {
			return this.salary.annual_salary();
		}
	}
}

let emp = Employee('Max', 25)
emp.salary = new Salary(15000, 10000)
console.log(emp.total_salary())

 
 
 

Вопрос 1 из 3

Была ли вам полезна эта статья?
[50 / 4.7]

Spread the love
Подписаться
Уведомление о
guest
9 Комментарий
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Василий
Василий
4 лет назад

Вопрос по типам отношений Ассоциация и Агрегация. У вас в примере в случае Ассоциации класс Logger имеет поле stream с ссылкой на объект «stdout» и в случае Агрегации в вашем примере класс Logger имеет поле stream с ссылкой на объект «stdout». В чем тут разница?

Гоша
Гоша
4 лет назад

Примеры непонятны. Описание хорошее, а примеры полная херня.

Анонимно
Анонимно
4 лет назад

Дважды: pay, pay. Это опечатка?
Employee::constructor(name, age, pay, pay)

Editorial Team
Администратор
4 лет назад

Да была опечатка. Спасибо, поправил.

Анонимно
Анонимно
3 лет назад

добавь плиз схему как это будет выглядеть в UML. Спасибо!

Aleksey
Aleksey
3 лет назад

Классный пример!!! просто огонь! всё понял сразу, правда я уже прочитал 8 главу в книге Вайсфилда( ООП мышление ), несколько статей и только когда были примеры на моём любимом JavaScript-e я всё смог понять

композиция — это когда мы создаём экземпляр объекта в конструкторе (тут всё просто)

агрегация — это когда мы кладём объект в момент создания экземпляра класса типо 
new Obj(new SomeObj())

ассоциация — это когда мы можем изменять класс с которым мы взаимодействуем в процессе работы объекта, мне не совсем понравился пример автора там где он без интерфейса работает с объектом на прямую к свойству, это не по ООП-шному, по этому я сделал следующий пример
obj.setAssociation(new Object());
obj.getAssociation().someMethod();

obj.setAssociation(new SomeObj());
obj.getAssociation().someMethod();

Last edited 3 лет назад by Aleksey
Анонимно
Анонимно
3 лет назад

привет, не могли вы объяснить, почему не выводится в консоль данную строку (агрегация).

‘use strict’;

class Salary {

constructor(pay, bonus) {

this.pay = pay;

this.bonus = bonus;

}

annualSalary() {

return (this.pay * 12) + this.bonus;

}

}

class Employee {

constructor(name, age, salary) {

this.name = name;

this.age = age;

this.salary = salary;

}

totalSalary() {

if (this.salary) {

return this.salary.annualSalary();

}

}

}

let salary = new Salary(15000, 10000);

let emp = Employee(‘Max’, 25, salary);

console.log(emp.totalSalary());

Class constructor Employee cannot be invoked without ‘new’

но когда пишу new то выводится 190000, и больше ничего.

Иван
Иван
2 лет назад

Спасибо за статью! Узнал много полезного. Но обратите пожалуйста внимание на грамматику. Сложно читать текст с ошибками. Сбивает с мысли.

Димас
Димас
2 лет назад

Один экземпляр мебели может принадлежать с начал!О!? одной комнате потом другой.
Серьезно???? Что за школота писила?