使用John Papa的AngularJS风格指南,什么是声明数据对象的正确方法?

时间:2022-03-09 03:56:13

Let's say that I have an AngularJS data service that makes a call to the server and returns an object that can be extended with additional methods. For example, assume the following function is part of an AngularJS service for something like NerdDinner.

假设我有一个AngularJS数据服务,它调用服务器并返回一个可以通过其他方法扩展的对象。例如,假设以下函数是NerdDinner之类的AngularJS服务的一部分。

function getDinner(dinnerId) {
   return $http.get('api/dinner/' + dinnerId)
      .then(loadDinnerComplete)
      .catch(loadDinnerFailed);

   function loadDinnerComplete(response) {
      return new Dinner(response.data);
   }
}

What's the best practice for a place to define the Dinner class? Is that a factory in a separate file? Do I define that in the NerdDinner service? Or do I define that in the GetDinner class (assuming that's the only method that can create dinners)?

一个地方定义晚餐类的最佳实践是什么?那是一个单独文件中的工厂吗?我在NerdDinner服务中定义它吗?还是在GetDinner类中定义它(假设这是惟一可以创建dinner的方法)?

I did not find any specific reference to creating objects in the style guide, so forgive me if it's covered and I just missed it.

我没有在样式指南中找到创建对象的任何特定引用,所以如果包含了,请见谅,我只是错过了它。

Edit I ultimately decided to accept Jeroen's answer because it most matched my needs for a rather simple use case. However, Daniel's answer is pure gold, and should not be overlooked. If I chose to extend the functionality of my DTO with simple CRUD or additional server based operations, a $resource is a great approach.

编辑我最终决定接受Jeroen的回答,因为它最符合我对一个相当简单的用例的需求。但以理的回答是纯金的,不可忽视。如果我选择使用简单的CRUD或其他基于服务器的操作来扩展DTO的功能,那么$resource是一种很好的方法。

3 个解决方案

#1


7  

Where to place business entities (like Dinner) is not explicitly mentioned in John Papa's style guide (afaik).

在哪里放置商业实体(比如晚餐)在John Papa的风格指南(afaik)中没有明确提及。

If you want to go this route (using business entities and placing logic there), I should give each entity its own factory:

如果你想走这条路(使用业务实体并在那里放置逻辑),我应该给每个实体一个自己的工厂:

(function() {
  'use strict';

  angular
    .module('myDinner')
    .factory('Dinner', DinnerFactory);

  function DinnerFactory() {

    Dinner.prototype.eat = eat;
    Dinner.prototype.cancel = cancel;

    return Dinner;

    // Constructor

    function Dinner (data) {
        // this is just an example:
        this.time = data.time;
        this.location = data.location;
    }

    // Methods

    function eat() {
      // ...
    }

    function cancel() {
      // ...
    }

  }

})();

Then you can inject them into your controller or maybe other service objects, just using Dinner, and create a new Dinner object using new Dinner(data).

然后,您可以将它们注入到控制器或其他服务对象中,只需使用Dinner,然后使用new Dinner(数据)创建一个新的Dinner对象。

You could also use a service, but John Papa discourages services, because they are too similar to factories.

你也可以使用服务,但是John Papa不鼓励服务,因为它们太像工厂了。

#2


4  

IMO what you are doing has already been done in $resource and restacular. You are talking about "defining data objects" (or, models in typical parlance). That said, defining each "data object" as its own factory is the way to do it, as per John Papa's advice regarding single concerns.

在我看来,你所做的一切已经在$resource和restacular中完成了。您正在讨论的是“定义数据对象”(或者,典型的说法是模型)。也就是说,将每个“数据对象”定义为自己的工厂是实现这一目标的方法,正如John Papa关于单个关注点的建议。

John Papa talks about this in the factories section.

约翰·帕帕帕在工厂区谈到了这一点。

If you want to work this out by hand, IMO you could define your models and append methods on each which would represent various crud operations. This is the pattern $resource and restangular (sort of) take.

如果您想手工解决这个问题,IMO您可以定义您的模型和附加方法,它们将代表各种crud操作。这是$resource和restangle(有点)的模式。

//Dinner model as angular factory, each of these methods returns a promise

function Dinner($http) {
    return {
        create: function(route, body) { /** http.post */ },
        get: function(route) { /** http.get */ },
        update: function(route, body) { /** http.put */ },
        destroy: function(route) { /** http.delete */ }
    };
}

now your Dinner model has convenient crud methods built in so you could do

现在,您的晚餐模型内置了方便的crud方法,因此您可以这样做

var dinner = new Dinner;
dinner.get("/api/dinner/1").then() //get dinner with id of 1
dinner.update("/api/dinner/1", {name: "burger"}).then() //update dinner with id of 1

Edit:

编辑:

So if you want to create an object that isn't concerned with retrieving data, IMO you should create another factory that requires your model. This decouples your data retrieval from your data manipulation. In OPs original example, the data retrieval and manipulation are tightly coupled.

因此,如果您想创建一个与检索数据无关的对象,在我看来,您应该创建另一个需要您的模型的工厂。这将数据检索与数据操作分离开来。在OPs的原始示例中,数据检索和操作是紧密耦合的。

function Meal(dinner) {
    //this.meal is the specified dinner
    this.meal = new Dinner().get("/api/dinner" + dinner);

    //some random build-in data manipulation methods
    return {
        getCalories: function() { return this.meal * 400; },
        getPrice: function() { return (this.meal * 100) + "$"; }
    };
}

Now that you've separated your data-manipulation into a separate object you can do something like this (this is a synchronous example though)

现在您已经将数据操作分离到一个单独的对象中,您可以执行以下操作(尽管这是一个同步示例)

var mcdonalds = new Meal(/** specify which dinner */)
mcdonalds.getPrice() //$4.56
mcdonalds.getCalores() //9999

#3


2  

Personally I have used both Services and Factories for creating objects. From John Papa's style guide however:

我个人使用了服务和工厂来创建对象。从John Papa的风格指南:

Services are instantiated with the new keyword, use this for public methods and variables. Since these are so similar to factories, use a factory instead for consistency.

服务用new关键字实例化,用于公共方法和变量。由于它们与工厂非常相似,所以使用工厂来代替一致性。

Following the SRP, you should put it in a new Service or Factory not part of another.

遵循SRP,您应该将它放在一个新的服务或工厂中,而不是另一个的一部分。

#1


7  

Where to place business entities (like Dinner) is not explicitly mentioned in John Papa's style guide (afaik).

在哪里放置商业实体(比如晚餐)在John Papa的风格指南(afaik)中没有明确提及。

If you want to go this route (using business entities and placing logic there), I should give each entity its own factory:

如果你想走这条路(使用业务实体并在那里放置逻辑),我应该给每个实体一个自己的工厂:

(function() {
  'use strict';

  angular
    .module('myDinner')
    .factory('Dinner', DinnerFactory);

  function DinnerFactory() {

    Dinner.prototype.eat = eat;
    Dinner.prototype.cancel = cancel;

    return Dinner;

    // Constructor

    function Dinner (data) {
        // this is just an example:
        this.time = data.time;
        this.location = data.location;
    }

    // Methods

    function eat() {
      // ...
    }

    function cancel() {
      // ...
    }

  }

})();

Then you can inject them into your controller or maybe other service objects, just using Dinner, and create a new Dinner object using new Dinner(data).

然后,您可以将它们注入到控制器或其他服务对象中,只需使用Dinner,然后使用new Dinner(数据)创建一个新的Dinner对象。

You could also use a service, but John Papa discourages services, because they are too similar to factories.

你也可以使用服务,但是John Papa不鼓励服务,因为它们太像工厂了。

#2


4  

IMO what you are doing has already been done in $resource and restacular. You are talking about "defining data objects" (or, models in typical parlance). That said, defining each "data object" as its own factory is the way to do it, as per John Papa's advice regarding single concerns.

在我看来,你所做的一切已经在$resource和restacular中完成了。您正在讨论的是“定义数据对象”(或者,典型的说法是模型)。也就是说,将每个“数据对象”定义为自己的工厂是实现这一目标的方法,正如John Papa关于单个关注点的建议。

John Papa talks about this in the factories section.

约翰·帕帕帕在工厂区谈到了这一点。

If you want to work this out by hand, IMO you could define your models and append methods on each which would represent various crud operations. This is the pattern $resource and restangular (sort of) take.

如果您想手工解决这个问题,IMO您可以定义您的模型和附加方法,它们将代表各种crud操作。这是$resource和restangle(有点)的模式。

//Dinner model as angular factory, each of these methods returns a promise

function Dinner($http) {
    return {
        create: function(route, body) { /** http.post */ },
        get: function(route) { /** http.get */ },
        update: function(route, body) { /** http.put */ },
        destroy: function(route) { /** http.delete */ }
    };
}

now your Dinner model has convenient crud methods built in so you could do

现在,您的晚餐模型内置了方便的crud方法,因此您可以这样做

var dinner = new Dinner;
dinner.get("/api/dinner/1").then() //get dinner with id of 1
dinner.update("/api/dinner/1", {name: "burger"}).then() //update dinner with id of 1

Edit:

编辑:

So if you want to create an object that isn't concerned with retrieving data, IMO you should create another factory that requires your model. This decouples your data retrieval from your data manipulation. In OPs original example, the data retrieval and manipulation are tightly coupled.

因此,如果您想创建一个与检索数据无关的对象,在我看来,您应该创建另一个需要您的模型的工厂。这将数据检索与数据操作分离开来。在OPs的原始示例中,数据检索和操作是紧密耦合的。

function Meal(dinner) {
    //this.meal is the specified dinner
    this.meal = new Dinner().get("/api/dinner" + dinner);

    //some random build-in data manipulation methods
    return {
        getCalories: function() { return this.meal * 400; },
        getPrice: function() { return (this.meal * 100) + "$"; }
    };
}

Now that you've separated your data-manipulation into a separate object you can do something like this (this is a synchronous example though)

现在您已经将数据操作分离到一个单独的对象中,您可以执行以下操作(尽管这是一个同步示例)

var mcdonalds = new Meal(/** specify which dinner */)
mcdonalds.getPrice() //$4.56
mcdonalds.getCalores() //9999

#3


2  

Personally I have used both Services and Factories for creating objects. From John Papa's style guide however:

我个人使用了服务和工厂来创建对象。从John Papa的风格指南:

Services are instantiated with the new keyword, use this for public methods and variables. Since these are so similar to factories, use a factory instead for consistency.

服务用new关键字实例化,用于公共方法和变量。由于它们与工厂非常相似,所以使用工厂来代替一致性。

Following the SRP, you should put it in a new Service or Factory not part of another.

遵循SRP,您应该将它放在一个新的服务或工厂中,而不是另一个的一部分。