Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Задачка на понимание ООП (https://javascript.ru/forum/misc/54362-zadachka-na-ponimanie-oop.html)

Ivan_Ivanoff_24 14.03.2015 15:52

Задачка на понимание ООП
 
Всем доброго времени суток!

Читая и осваивая JavaScript, конкретно, ООП-раздел, наткнулся на короткую, но пока неподъёмную для меня задачку.

"В системе имеется некоторое число объектов, которые сообщаются между собой с помощью событий.
Написать на native JavaScript класс, который будет предоставлять им эту возможность.
Реализовать на примере."


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

Предварительные идеи - при создании нового объекта (объекты одного класса) на этапе конструктора записывать новый объект в массив объектов (изначально он будет пуст). Далее по логике каждый объект должен иметь методы наподобие SendMessage (здесь можно разбить на варианты SendToOne И SendToAll - то есть либо всем объектам массива либо конкретному) и ReceiveMessage - получение сообщения от объектов.

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

Буду благодарен за любые идеи и рекомендации. Готового кода не прошу, просто опишите, как вы видите решение такой задачи.

Благодарю! :)

nuboquest 14.03.2015 16:30

Ivan_Ivanoff_24,
может быть паттерн "наблюдатель" (observer) имеется в виду?

nuboquest 14.03.2015 16:35

Цитата:

Сообщение от Ivan_Ivanoff_24
"В системе имеется некоторое число объектов, которые сообщаются между собой с помощью событий.
Написать на native JavaScript класс, который будет предоставлять им эту возможность.
Реализовать на примере."

Не совсем ясно из постановки, должны ли все объекты как-то реагировать на сообщения, или просто "знать" какое то состояние. Выложите задание полностью.
Цитата:

Сообщение от Ivan_Ivanoff_24
как реализовать получение и отсылку сообщений?

Это реализовывать не надо:). JS -- это ООП-язык, там и так есть объекты и сообщения.

tsigel 14.03.2015 16:39

В javascript нет событий если не считать Dom, но никто не мешает их эмулировать. События это объект который хронит в себе ассоциативный массив (где ключ это имя события) массив обработчиков. Это совсем не сложно.

nuboquest 14.03.2015 16:59

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

tsigel 14.03.2015 17:18

/**
 * @class Base
 */
var Base = function Base() {};
Base.prototype = {

    /**
     * @private
     */
    _events: null,

    /**
     * @method trigger
     * @param {String} eventName
     * @param {*|Array} [arg]
     * @returns @this
     */
    trigger: function (eventName, arg) {

        var $Event = function (type) {
            this.type = type;
        };

        if (!this._events || !this._events[eventName]) {
            return this;
        }

        if (!(arg instanceof Array)) {
            arg = [arg];
        }

        var iteration = function () {

            var arrEvents = this._events[eventName].slice();

            arrEvents.forEach(function (handler) {
                handler.apply(new $Event(eventName), arg);
            });

        }.bind(this);

        iteration();


        return this;
    },

    /**
     * @method bind
     * @param {String|Object} eventName
     * @param {function|Array} [handler]
     * @returns @this
     */
    bind: function (eventName, handler) {

        if (eventName == undefined) {
            console.log("Неверные параметры!");
            return this;
        }

        if (typeof eventName == "object") {
            this.iterationObj(eventName, function (handler, eventName) {
                this.bind(eventName, handler);
            }.bind(this));
            return this;
        }

        if (handler == undefined) {
            console.log("Неверные параметры!");
            return this;
        }

        if (!this._events) {
            this._events = {};
        }

        if (!this._events[eventName]) {
            this._events[eventName] = [];
        }

        if (!(handler instanceof Array)) {
            this._events[eventName].push(handler);
        } else {
            this._events[eventName] = this._events[eventName].concat(handler);
        }

        return this;
    },

    /**
     * @method unbind
     * @param {String|Object} [eventName]
     * @param {function} [handler]
     * @returns @this
     */
    unbind: function (eventName, handler) {

        if (eventName == undefined) {
            this._events = {};
            return this;
        } else if (eventName instanceof Object) {
            for (var key in eventName)
                if (eventName.hasOwnProperty(key))
                    this.unbind(key, key[eventName]);

            return this;
        }

        if (handler == undefined) {
            if (this._events && eventName in this._events) {
                this._events[eventName] = [];
                return this;
            }
            return this;
        }

        for (var i = 0; i < this._events[eventName].length; i++) {

            if (this._events[eventName][i] == handler) {
                this._events[eventName].splice(i, 1);
                break;
            }

        }

        return this;
    }

};


var Obj1 = new Base();

Obj1.bind("some event", function () {
  alert("some event!");
});

Obj1.bind("some event", function () {
  alert("some event!2");
});

Obj1.trigger("some event", []);


Раньше я использовал этот объект как базовый класс. Я наследовался от него и все мои объекты могли общаться событиями. Сейчас я использую события взятые из backbone (хотя в целом там тоже самое)

nuboquest 14.03.2015 17:21

Цитата:

Сообщение от Ivan_Ivanoff_24
чтобы объект знал, от кого ему пришло сообщение, сохранял его у себя в каком-то свойстве и мы могли бы извлечь эти данные. Ну и не всё понятно с механизмом отправки.

O={
 sendTo: function(obj, msg){obj.receiveFrom(this, msg)},
 receiveFrom: function(sender, msg){console.log(this.name+": I have receive the message '"+msg+"' from "+sender.name)},
 create: function(name){var o=Object.create(this); window[name]=o; o.name=name}
}

O.create("object1")
O.create("object2")

object1.sendTo(object2, "Hello!")

//  object2: I have receive the message 'Hello!' from object1

nuboquest 14.03.2015 18:02

Цитата:

Сообщение от tsigel
если не считать Dom

Да, еще если не считать событий мыши, событий сети, событий клавиатуры, и еще over100000 событий, и забываем, что серверный JS существует еще внезапно, переполненный своими событиями. А так, да, чисто дом. Так оно и надо писать: new Ko-ko-ko dom ko-ko-ko.

tsigel 14.03.2015 18:20

Цитата:

Сообщение от nuboquest
Да, еще если не считать событий мыши, событий сети, событий клавиатуры

nuboquest, вообще-то это и есть dom события

nuboquest 14.03.2015 18:25

tsigel,
Ну и какое же они имеют отношение к документу? Если какой то придурок когда то что-то как-то назвал, другие не обязаны повторять за ним эту глупость.

Erolast 14.03.2015 18:52

Цитата:

Но есть вопросы по реализации
Как образец можно взять реализацию из ноды/io.js, вот апи - https://iojs.org/api/events.html

Цитата:

SendToOne
Методы принято называть с маленькой буквы.

Цитата:

Читая и осваивая JavaScript, конкретно, ООП-раздел
Как закончишь, изучи и освой классы из ES6 - http://www.2ality.com/2015/02/es6-classes-final.html
ES6 пока только готовится к релизу, но им уже можно пользоваться почти на полную мощь с помощью трансляции.

Ivan_Ivanoff_24 14.03.2015 22:23

Цитата:

Сообщение от nuboquest
Не совсем ясно из постановки, должны ли все объекты как-то реагировать на сообщения, или просто "знать" какое то состояние. Выложите задание полностью.

К сожалению, это и есть полностью( Поэтому и обратился к Вам, чтобы понять, насколько различно можно толковать его условие.

Цитата:

Сообщение от nuboquest
O={
        sendTo: function(obj, msg){obj.receiveFrom(this, msg)},
        receiveFrom: function(sender, msg){console.log(this.name+": I have receive the message '"+msg+"' from "+sender.name)},
 create: function(name){var o=Object.create(this); window[name]=o; o.name=name}
	}
	 
	O.create("object1")
     O.create("object2")
 
       object1.sendTo(object2, "Hello!")
 
	//  object2: I have receive the message 'Hello!' from object1

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

Ivan_Ivanoff_24 14.03.2015 22:30

Цитата:

Сообщение от tsigel
В javascript нет событий если не считать Dom, но никто не мешает их эмулировать. События это объект который хронит в себе ассоциативный массив (где ключ это имя события) массив обработчиков. Это совсем не сложно.

Спасибо за ответ, и если это возможно, не могли бы Вы немного подробнее описать процесс эмуляции события? Либо посоветовать какие-то материалы на эту тему.

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

Благодарю!

Ivan_Ivanoff_24 14.03.2015 22:31

Erolast, Благодарю за ЦУ, обязательно учту и воспользуюсь =)

nbq 14.03.2015 23:02

Цитата:

Сообщение от Ivan_Ivanoff_24
Такой вариант ближе всего к моему представлению этого процесса). Вопрос - в этом варианте объект О можно считать тем самым классом, который предоставляет возможность взаимодействовать объектам (как в условии задания) либо необходимо что-то добавлять? Заранее прошу прощения за дилетантские вопросы.

Это класс по-сути. В нормальных ООП языках классы -- первоклассные объекты. Вопрос только в том, кто Вас будет проверять. Не у Всех есть способность воспринимать вещи без сахара.

В принципе, Вы можете добавить сахарку:

O=function(name){this.name=name}
O.prototype={
 sendTo: function(obj, msg){obj.receiveFrom(this, msg)},
 receiveFrom: function(sender, msg){console.log(this.name+": I have receive the message '"+msg+"' from "+sender.name)},
}

 
object1=new O("object1")
object2=new O("object2")
 
object1.sendTo(object2, "Hello!")
//  object2: I have receive the message 'Hello!' from object1

Тогда формально не докопаешься.

Ivan_Ivanoff_24 15.03.2015 00:07

nbq, спасибо огромное! Заодно подробнее ознакомился с prototype и увидел, как можно красивее "завернуть" туда функционал.

Ivan_Ivanoff_24 15.03.2015 00:49

Цитата:

Сообщение от nuboquest (Сообщение 361239)
Ivan_Ivanoff_24,
может быть паттерн "наблюдатель" (observer) имеется в виду?

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

Erolast 15.03.2015 08:29

Цитата:

O=function(name){this.name=name}
O.prototype={
sendTo: function(obj, msg){obj.receiveFrom(this, msg)},
receiveFrom: function(sender, msg){console.log(this.name+": I have receive the message '"+msg+"' from "+sender.name)},
}


object1=new O("object1")
object2=new O("object2")

object1.sendTo(object2, "Hello!")
// object2: I have receive the message 'Hello!' from object1
Только
  1. Начиная с ES5, переменные без var объявлять запрещено, в строгом режиме код не заработает.
  2. Добавление методов реализовано неверно: во-первых, у прототипа класса должно быть свойство constructor, ссылающееся на сам класс (так и есть по умолчанию, но здесь оно перезаписано); во-вторых, все свойства прототипа должны быть неперечисляемы:
    function defineMethod(target, name, value) {
        Object.defineProperty(target.prototype, name, {
            value: value,
            enumerable: false
        })
    }
    
    function Animal() {
      this.speed = 0;
    }
    defineMethod(Animal, "run", function() {
      this.speed++;
    });
    
    (new Animal()).run();
    
  3. have recieved

tsigel 16.03.2015 12:37

Цитата:

Сообщение от Ivan_Ivanoff_24 (Сообщение 361299)
Спасибо за ответ, и если это возможно, не могли бы Вы немного подробнее описать процесс эмуляции события? Либо посоветовать какие-то материалы на эту тему.

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

Благодарю!

Объект Base служит как базовый класс, от которого наследуются мои объекты получая возможность слушать и генерировать события.
Он имеет 3 метода:
1) bind - подписываемся на события. Он принимает либо собыитие и обработчик, либо объект, в котором ключ - это имя события, а значение - обработчик. Пример использования:

new Base().bind("some event", function handler() {...});
  new Base().bind({
    "some event 1": function handler1() {...},
    "some event 2": function handler2() {...}
  });


2) unbind - отписываемся от событий.

var baseExtended = ... //объект с подписанными обработчиками.

   baseExtended.unbind() // снимет все обработчики со всех событий объекта

   baseExtended.unbind("event") // снимет все обработчики с именем события "event";

   baseExtended.unbind("event", handler) // снимит конкретный обработчик который равен передаваемой функции (как removeEventListener)


3) - trigger - метод который запускает события (1-й аргумент - имя события, второй массив аргуметов которые придут в функции - слушатели);

baseExtended.trigger("event", [baseExtended]) // запускаем событие "event", всем слушателям придет первым аргументом автор события


Часовой пояс GMT +3, время: 23:44.