Абстракция в javascript. Приватные свойства и методы.

3

Abstraction

Допустим у нас есть объект Circle, и в нём есть некоторые свойства и методы — см. ниже:

function Circle(radius) {
  this.radius = radius;

  this.defaultLocation = {x: 0, y: 0};
  this.computeOptimumLocation = function () {
      //..
  } 

  this.draw = function() {
    this.computeOptimumLocation();  
    console.log('draw');
  }
}

Вызовем метод computeOptimumLocation из методы draw. Но есть некоторая проблема в этой реализации.

Допустим мы создали объект circle

const circle = new Circle(100);

Прикол в том, что не все свойства этого объекта должны быть доступны конечному пользователю, клиенту. Например, что случится, если мы укажем defaultLocation как false — это полностью испортит этот объект

circle.defaultLocation = false;

Или другой пример, может быть этот метод — computeOptimumLocation должен вызываться только в внутри метода draw. Если мы случайно его вызовем просто из объекта

circle.computeOptimumLocation();
circle.draw();

Может это испортит объект. И затем, когда мы вызовем circle.draw() — получим странную ошибку в runtime.

В объектно-ориентированном программировании есть одна из основных концепций, называемая абстракцией.

Абстракция означает, что мы должны прятать детали и сложность и показывать только необходимое.

В нашем конкретном примере, нам нужно спрятать свойство defaultLocation например в методе computeOptimumLocation.

Мы хотим предоставить на пользование только свойство radius и метод draw.

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

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

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

Итого

Запомните один из главных принципов ООП — Абстракции, которая гласит:

Скрывайте детали, и оставляйте в доступе только необходимые штуковины.

Мы подошли к тому как реализовать это в Java Script.

Приватные свойства и методы (Private properties and Methods)

И так, как нам реализовать абстракцию на нашем примере. Нам нужно спрятать некоторые херни от всеобщего взора. Как вы знаете, this указывает на новосозданный объект. Что произойдет, если мы укажем локальную переменную в функции Circle?

Например, let color = red;

function Circle(radius) {
  let color = red;
  this.radius = radius;
//...

Будет ли это частью объекта? Нет, потому что мы не указали его как свойство объекта, мы не установили color как свойство this. Т.е. это локальная переменная функции Circle. Когда мы выходим из этой функции, эта переменная выходит из области видимости (scope) и умирает ). Теперь, таким образом, мы можем легко спрятать определенные вещи от окружающего мира. Итак, defaultLocation это детали реализации и мы не хотим, чтобы к ним был доступ извне. Вместо того, чтобы указывать его свойство нового объекта, мы определим его как новую переменную. Таким же образом, мы можем переделать метод computeOptimumLocation в приватный метод. Итак мы вызываем этот метод из метода draw как обычную функцию. Это будет работать благодаря такой прекрасной штуке как closure или замыканию.

function Circle(radius) {
  //Определили как новую переменную
  let defaultLocation = {x: 0, y: 0};
  let computeOptimumLocation = function () {
      //...
  } 

  this.draw = function() {
    computeOptimumLocation();  
    console.log('draw');
  }
}

Если вы не знаете, что такое замыкание, то…

В двух словах, вот у нас есть одна функция Circle и другая функция (draw) внутри этой функции. Внутри этой функции мы можем объявить определенные переменные, например x и y.

this.draw = function() {
  let x,y;
  //..

И эти локальные переменные доступны только в этой функции. Их область видимости ограничена этой функцией. И когда мы закончим выполнение этой функции. x и y уйдут из области видимости (scope). В противовес scope у нас есть closure (замыкание). Зымыкание определяет какие переменные будут доступны внутренней функции. Т.е функции draw будут доступны все её переменные, а также переменные её родителей. И так мы объявили две переменные computeOptimumLocation и defaultLocation. Они определены в родительской функции, и они есть в области видимости этой функции и в пределах замыкания внутренней функции. Итак не путайте scope с closure. Потому что scope временный и затем умирает.

Каждый раз когда мы будет вызывать метод draw эти переменные (x, y) будут создаваться и переинициализироваться, и затем после выполнения функции они будут мертвы. Но замыкание никуда не денется. Когда мы вызовем метод draw , после того как мы закончим выполнение функции, эти переменные (computeOptimumLocation и defaultLocation) продолжат оставаться в памяти. Они сохранят свое состояние, потому что они часть замыкания метода draw.

You might also like More from author

Leave A Reply

Your email address will not be published.