Как организовать ООП-логику на JavaScipt на примере аудио-плеера?

Всем привет, ребята. Суть в том, что пишется большой аудио-видео плеер, в котором уйма элементов интерфейса и событий. Все модули - родные, которые были написаны самостоятельно на чистом JS(конструкторы их можно увидеть чуть ниже). Примерное кол-во строк кода до рефакторинга - 3500. На сколько верна данная структура для такого проекта и какие дизайнерские решения можно тут использовать? P.S. код набросан просто для наглядности того, как организована структура(в действительности все объемнее и менее читаемо).

var PlayerApp = (function() {
  // В замыкании храним очень много элементов интерфейса и модулей
  var audio = null;
  var startButton = $('button.start');
  var audioAnalyser = new AudioAnalyser(audio);
  var cvsVisualizator = new CanvasVisualizator($('#audio-visualization'));

  function bindEvents() {
    startButton.addEventListener('click', startPlayHandler);
    // Куча других привязок к событиям

    function startPlayHandler() {
      
    }
  }

  //Ниже конструкторы для канваса и web audio API, с помощью которого мы делаем спектр-визулизатор на канвасе

  function AudioAnalyser(audio) {
    //что-то делаем
  }

  function CanvasVisualizator(container) {
    //что-то делаем
  }
  return {
    init : function() {  
      bindEvents();
      //...
   },
    play : function() {

   }
   // остальные публичные методы
  }
})();
  • Вопрос задан
  • 506 просмотров
Пригласить эксперта
Ответы на вопрос 3
Fesor
@Fesor
Full-stack developer (Symfony, Angular)
Вы же понимаете что это никакого отношения к ООП не имеет? Это просто объекты содержащие функции, не настоящие объекты. Ну то есть у вас нет конструкторов, у вас есть "модули".

Да и как бы такие вещи как валидация и т.д. могут быть организованы как чистые функции, минимизируйте мутации состояния и будет проще.

Так же используйте модули, они помогут вам убрать лишние уровни вложенности и добиться изоляции, код станет опрятнее.

Меньше императивной логики, больше декларативного поведения. Если событий реально много можно попробовать посмотреть в сторону observable и ивент стримов.
Ответ написан
@floydback
Отличный вопрос! Сам долго задавался этой проблемой. Как бекендщик, понимал необходимость но не знал как реализовать структуру, чтобы не было каши.

Решение, которое помогло: нужно добиться того, чтобы в PlayerApp вообще не было обращений в DOM. Кстати, тогда и jQuery не нужен будет внутри PlayerApp. Там должно быть только контекст аудио, ноды, аналайзер и проч. Вот только это сделает код намного проще, чище и понятнее.

Например, PlayerApp.js может выглядеть так
(function() {
	var PlayerApp, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments) } }

	PlayerApp = (function() {
		function PlayerApp(url) {
			this.play = __bind(this.play, this)
			// другие __bind здесь, stop, resume, etc...

			this.audio = new Audio()
			this.audio.volume = 1
			this.audio.src = url

			this.audio.addEventListener("canplaythrough", this.canplay,  false)
			// другие addEventListener здесь: loadedmetadata, timeupdate, etc...
		}
		Track.prototype.play = function() {
			this.audio.play()
			if (typeof this.onplay_callback == "function") { this.onplay_callback() }
		}
		// другие функции здесь .... 
		return PlayerApp
	})()
	window.PlayerApp = PlayerApp
}).call(this)

А где-то в app.js будет графическая часть :
player = new PlayerApp()
player.onplay_callback = function() {
    $("#main-play-btn").toggleClass('play')
    $(".playlist-play-btn").toggleClass('play')
}

Таким образом, где бы ни запустили плей (кнопка в плеере, по центру экрана или пробелом на клавиатуре) отрисовка идет через одно место - колбэк onplay_callback. Через одно место ;) Смешно, но это очень упростило код мне.

Еще отлично организовано в Яндексе, посмотрите пример. Очень рекомендую.
Ответ написан
Комментировать
VGrabko
@VGrabko
Golang, Php, Js
вместо функций используйте замыкания.
как то так
var Valid = (function () {
    //import dom
    var Rule = {
        required: function (string) {
            if (string.length > 0) {
                return true;
            }
            return false;
        },
        min: function (string, count) {
            if (string.length > count) {
                return true;
            }
            return false;
        },
        max: function (string, count) {
            console.info(count);
            console.info(string.length);
            if (string.length <= count) {
                return true;
            }
            return false;
        },
        email: function (string){
            var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
            return re.test(string);
        }
    };
    var cc = function (string, rule, id, message, template) {
        var rules = rule.split("|");
        for (var i = 0; i < rules.length; i++) {
            if (Rule[rules[i]] === undefined) {
                var rulesP = rules[i].split(":");
                if (Rule[rulesP[0]] === undefined) {
                    console.warn("У валидатора нету правила " + rulesP[0]);
                } else {
                    
                    if (!Rule[rulesP[0]](string, rulesP[1])) {
                        dom.UpID(id, template[0]+message[rulesP[0]]+template[1]);
                        return false;
                    }
                }
            } else {
                if (!Rule[rules[i]](string)) {
                    dom.UpID(id, template[0]+message[rules[i]]+template[1]);
                    return false;
                }
            }
        }
        return true;
    };

    return {
        Make: function (rule, message, template) {
            var key;
            for (key in rule) {
                var id = key + "_error";
                dom.ClearID(id);
                //ошибка. Завершаем валидацию.
                if (!cc(dom.form.GetInputID(key), rule[key], id, message[key], template)) {
                    return false;
                }
            }
            return true;
        }
    };
}());


дабы все рендилось в 1 проход.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы