如何修复此框架以便可以按任何顺序定义服务?

时间:2016-12-05 21:16:19

标签: javascript

关于这个简单框架设计的有效性,也许你认为这很糟糕,但我真的只是想让它工作并且学习某些设计好坏的难点。我想让它工作,即使它是一个糟糕的设计。

所以我面临着一个挑战:如果根据另一个服务定义服务的代码在之前执行它依赖的服务,它会认为该组件不存在。因为该代码尚未执行。我的目标是能够以任何顺序执行定义服务的代码。

希望这有点道理。这个问题对我来说有点难以解释,但我的目标是编写如下代码:

new Service('machine', ['machine / components / gear'], function(gear) {
  alert('This machine depends on a ' + gear);
}).initialize();

new Service('machine / components / gear', ['machine / components / gear / components / cog'], function(cog) {
  return 'gear wich depends on a ' + cog;
});

new Service('machine / components / gear / components / cog', [], function(cog) {
  return 'cog';
});

为了这个问题,提供给Service构造函数的第一个参数是服务的名称。我最初编写框架的方式,在这种情况下'machine''machine / components / gear'之前执行,因此它看不到'machine / components / gear'存在,同样地,'machine / components / gear'之前执行'machine / components / gear / components / cog'因此无法使用该组件。

注意,在显示代码之前:你会看到框架中使用的count变量,以及一些奇怪的东西,基本上它都非常简单和整洁,直到我开始尝试缓解这个问题我是有。正如您将看到的那样,我试图解决问题,当找不到依赖项时,使用setTimeout并再次尝试,直到count增加几次,因此事件循环已完成几次,也许其他服务已完成执行。这很麻烦,可能是一种可怕的方式。

以下是代码:

var Directory = function() {
  this.directories = {};
  this.items = {};
};
var root = new Directory();
var Service = function(name, dependencies, func, count) {
  if (!count) {
    count = 0;
  }
  var that = this;
  this.name = name;
  this.wait = false;
  this.dependencyCollection = [];
  this.func = func;
  this.initialize = function() {
    setTimeout(function() {
      console.log('Initializing with dependency collection: '); //debugging
      console.log(that.dependencyCollection); //debugging
      that.func.apply(null, that.dependencyCollection);
    }, 10);
  };
  this.eventHandlers = {};
  this.trigger = function(name) {
    if (that.eventHandlers[name]) {
      that.eventHandlers[name]();
    }
  };
  this.on = function(name, func) {
    that.eventHandlers[name] = func;
  };
  this.mutate = function(dependencies, func) {
    new Service(that.name, dependencies, func);
    that.trigger('mutate');
  };
  for (var x = 0; x < dependencies.length; x++) {
    var nameSegments = dependencies[x].split(' / ');
    var service = root;
    var servicePlaceholder = service;
    for (var i = 0; i < nameSegments.length; i++) {
      var segment = nameSegments[i];
      if (i === nameSegments.length - 1) {
        if (servicePlaceholder.items[segment]) {
          console.log('Observing dependency: ' + servicePlaceholder.items[segment]());
          this.dependencyCollection.push(servicePlaceholder.items[segment]());
        } else {
          if (count < 3) {
            (function(count) {
              setTimeout(function() {
                count++;
                new Service(name, dependencies, func, count);
              }, 1)
            })(count);
          } else {
            throw 'Dependent item missing: ' + JSON.stringify(servicePlaceholder.items, null, 3) + ' (' + dependencies + ": " + segment + ')';
          }
        }
      } else if (servicePlaceholder.directories[segment]) {
        servicePlaceholder = servicePlaceholder.directories[segment];
      } else {
        if (count < 3) {
          (function(count) {
            setTimeout(function() {
              count++;
              new Service(name, dependencies, func, count);
            }, 1)
          })(count);
        } else {
          throw 'Dependent directory missing: ' + JSON.stringify(servicePlaceholder.directories, null, 3) + ' (' + segment + ')';
        }
      }
    }
  }
  var directory = root;
  var directoryPlaceholder = directory;
  var nameSegments = name.split(' / ');
  for (var i = 0; i < nameSegments.length; i++) {
    var segment = nameSegments[i];
    if (i === nameSegments.length - 1) {
      console.log('Observing dependency collection: ' + that.dependencyCollection);
      directoryPlaceholder.items[segment] = function() {
        return that.func.apply(null, that.dependencyCollection);
      }
    } else {
      if (directoryPlaceholder.directories[segment]) {
        directoryPlaceholder = directoryPlaceholder.directories[segment];
      } else {
        directoryPlaceholder.directories[segment] = new Directory();
        directoryPlaceholder = directoryPlaceholder.directories[segment];
      }
    }
  }
};

如果那里的任何内容没有意义,那就假设这是一个徒劳的尝试来缓解这个问题。我已经和它一起工作了好几个小时,最后决定我无处可去并寻求帮助。如果有人可以帮我弄清楚如何使这个(也许是可怕的)模型正确执行,我真的很感激。

没有超时和计数器以及诸如此类的代码的示例,只有在按时间顺序定义组件时才能正常工作:

var Directory = function() {
  this.directories = {};
  this.items = {};
};
var root = new Directory();
var Service = function(name, dependencies, func) {
  var that = this;
  this.name = name;
  this.wait = false;
  this.dependencyCollection = [];
  this.func = func;
  this.initialize = function() {
    that.func.apply(null, that.dependencyCollection);
  };
  this.eventHandlers = {};
  this.trigger = function(name) {
    if (that.eventHandlers[name]) {
      that.eventHandlers[name]();
    }
  };
  this.on = function(name, func) {
    that.eventHandlers[name] = func;
  };
  this.mutate = function(dependencies, func) {
    new Service(that.name, dependencies, func);
    that.trigger('mutate');
  };
  for (var x = 0; x < dependencies.length; x++) {
    var nameSegments = dependencies[x].split(' / ');
    var service = root;
    var servicePlaceholder = service;
    for (var i = 0; i < nameSegments.length; i++) {
      var segment = nameSegments[i];
      if (i === nameSegments.length - 1) {
        if (servicePlaceholder.items[segment]) {
          console.log('Observing dependency: ' + servicePlaceholder.items[segment]());
          this.dependencyCollection.push(servicePlaceholder.items[segment]());
        } else {
          throw 'Dependent item missing: ' + JSON.stringify(servicePlaceholder.items, null, 3) + ' (' + dependencies + ": " + segment + ')';
        }
      } else if (servicePlaceholder.directories[segment]) {
        servicePlaceholder = servicePlaceholder.directories[segment];
      } else {
        throw 'Dependent directory missing: ' + JSON.stringify(servicePlaceholder.directories, null, 3) + ' (' + segment + ')';
      }
    }
  }
  var directory = root;
  var directoryPlaceholder = directory;
  var nameSegments = name.split(' / ');
  for (var i = 0; i < nameSegments.length; i++) {
    var segment = nameSegments[i];
    if (i === nameSegments.length - 1) {
      console.log('Observing dependency collection: ' + that.dependencyCollection);
      directoryPlaceholder.items[segment] = function() {
        return that.func.apply(null, that.dependencyCollection);
      }
    } else {
      if (directoryPlaceholder.directories[segment]) {
        directoryPlaceholder = directoryPlaceholder.directories[segment];
      } else {
        directoryPlaceholder.directories[segment] = new Directory();
        directoryPlaceholder = directoryPlaceholder.directories[segment];
      }
    }
  }
};

setTimeout(function() {
  console.log(root);
}, 1000);

new Service('machine / components / gear / components / cog', [], function(cog) {
  return 'cog';
});

new Service('machine / components / gear', ['machine / components / gear / components / cog'], function(cog) {
  return 'gear wich depends on a ' + cog;
});

new Service('machine', ['machine / components / gear'], function(gear) {
  alert('This machine depends on a ' + gear);
}).initialize();

1 个答案:

答案 0 :(得分:1)

处理此问题的一种方法是将缺少依赖项的服务添加到队列中。每次注册新组件时,查看队列中的项是否具有所需的依赖项。

最后,为了捕获依赖解析的情况,使用setTimeout在所有脚本加载后检查队列,并报告任何从未有过依赖关系的组件解决。

&#13;
&#13;
var Service = (function() {
	var _registered = {};
  var _waiting = [];
  
  // Check if all dependencies have been resolved
  setTimeout(function() {
  	_waiting.forEach(function(obj) {
    	throw new Error('Failed to resolve dependencies for ' + obj.name);
    });
  }, 1);
  
  function allDependenciesLoaded(dependencies) {
  	return dependencies.every(function(dep) {
    	return !!_registered[dep];
    });
  }
  
  function processWaiting() {
  	for (var i = _waiting.length - 1; i >= 0; i--) {
    	var obj = _waiting[i];
      if (allDependenciesLoaded(obj.dependencies)) {
      	_waiting.splice(i, 1);
        obj.func();
      }
    }
  }
  
  return function(componentName, dependencies, func) {
  	_registered[componentName] = func;
    if (!allDependenciesLoaded(dependencies)) {
    	_waiting.push({
      	name: componentName,
        dependencies: dependencies,
        func: func
      });
    } else {
    	func();
    }
    processWaiting();
  };
})();

new Service('D', ['this', 'one', 'will', 'fail'], function() {});

new Service('A', ['B', 'C'], function() {
	console.log('A is ready');
});

new Service('B', [], function() {
	console.log('B is ready');
});

new Service('C', [], function() {
	console.log('C is ready');
});
&#13;
&#13;
&#13;