【问题标题】:Law of Demeter and angular controller DI得墨忒耳定律和角度控制器 DI
【发布时间】:2014-12-18 05:01:35
【问题描述】:

我正在阅读 http://misko.hevery.com/attachments/Guide-Writing%20Testable%20Code.pdf(尤其是第 8 页)并观看 Misko 关于编写可测试代码的 Youtube 视频,我突然想到 Angular 执行 DI 的方式迫使您违反 Demeter 定律。

从PDF简化,一个Java构造函数违反得墨忒耳定律的例子:

class AccountView {
  boolean isAdmin;
  AccountView(AccountService) {
    isAdmin = AccountService.getCurrentUser().getProfile().isAdmin();
  }
}

因为该类只需要用户是否是管理员,而不需要 AccountService。

似乎 Angular 用它的 DI 迫使你打破得墨忒耳定律。我看不到以下内容的替代方案:

.controller('AccountServiceController', 
  ['AccountService', 
  function(AccountService) {
    this.user = AccountService.getCurrentUser().getProfile().isAdmin();
  }]
);

如果这是一个控制器,一个控制器是一个具有解析参数的路由器,我们可以注入一个用户,但不适用于一般情况。有什么想法吗?请注意,我假设只有 AccountService 是一个单例,并且每个后续对象都是一个实例(并且不能是 DI)。

【问题讨论】:

    标签: angularjs law-of-demeter


    【解决方案1】:

    我不确定您提供的 Angular 示例或一般的 Angular DI 是否违反了 demeter 定律。 Angular 依赖注入允许您编写非常可测试的代码。

    假设真正的AccountService.getCurrentUser(); 是一个非常昂贵的网络操作。我们绝对不想在测试中调用这个方法。使用 Angular 的依赖注入,我们可以模拟 AccountService,所以我们不必调用真实的。

    让我们为AccountServiceController写一个测试:

    控制器

    .controller('AccountServiceController', 
      ['$scope', 'AccountService', 
      function($scope, AccountService) {
        $scope.user = AccountService.getCurrentUser();
      }]
    );
    

    测试

    describe('AccountServiceController function', function() {
    
      describe('AccountServiceController', function() {
        var $scope;
    
        beforeEach(module('myApp'));
    
        beforeEach(inject(function($rootScope, $controller) {
          $scope = $rootScope.$new();
          $controller('AccountServiceController', {
            $scope: $scope
            AccountService: {
              getCurrentUser: function () {
                return {
                  name: 'Austin Pray',
                  id: 1111
                };
              }
            }
          });
        }));
    
        it('should get the current user', function() {
          expect(typeof $scope.user.name).toBe('string');
        });
      });
    });
    

    我们避免了昂贵的网络操作,并且控制器没有以任何方式与AccountService 的内部耦合。

    【讨论】:

    • 我不认为它实际上会导致难以测试代码,但我会指出(从链接的 pdf 的第 21 页):“当一种方法作为一个定位器除了它的主要职责......你永远不必模拟一个 setter 或 getter。一个更好的例子可能是 AccountService.getCurrentUser().getProfile().isAdmin()。来自第 17 页:“违反“得墨忒耳法则”……症状:看到多个时期“。”在方法链接中,方法是 getter”。我的示例没有这个,但 Angular DI 在其他情况下强制它。
    • 你不能只做currentUserService 这样的服务,直到你只有一个点吗?
    • 您需要创建一个currentProfileService 才能到达一个点,但这也很臭,对吧?实际上,即使currentProfileService 也需要两个点 (currentProfileService.getCurrentProfile().isAdmin())。无论如何,现在我们似乎正在处理不必要的类扩散。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多