[service] AngularJS의 비 싱글 톤 서비스
AngularJS는 문서에서 Services가 Singleton이라고 명시합니다.
AngularJS services are singletons
반 직관적으로 module.factory
는 Singleton 인스턴스도 반환합니다.
단일 서비스가 아닌 서비스에 대한 사용 사례가 많다는 점을 감안할 때, ExampleService
종속성이 선언 될 때마다 다른 인스턴스에 의해 충족 되도록 서비스의 인스턴스를 반환하는 팩토리 메서드를 구현하는 가장 좋은 방법 은 ExampleService
무엇입니까?
답변
나는 new
이것이 의존성 주입을 분해하기 시작하고 라이브러리가 특히 제 3 자에게 어색하게 행동 할 것이기 때문에 공장에서 가능한 함수를 반환해야한다고 생각하지 않습니다 . 요컨대, 비단 일 서비스에 대한 합법적 인 사용 사례가 있는지 확실하지 않습니다.
동일한 작업을 수행하는 더 좋은 방법은 팩토리를 API로 사용하여 getter 및 setter 메서드가 연결된 개체 컬렉션을 반환하는 것입니다. 다음은 이러한 종류의 서비스를 사용하는 방법을 보여주는 의사 코드입니다.
.controller( 'MainCtrl', function ( $scope, widgetService ) {
$scope.onSearchFormSubmission = function () {
widgetService.findById( $scope.searchById ).then(function ( widget ) {
// this is a returned object, complete with all the getter/setters
$scope.widget = widget;
});
};
$scope.onWidgetSave = function () {
// this method persists the widget object
$scope.widget.$save();
};
});
이것은 ID로 위젯을 찾은 다음 레코드에 대한 변경 사항을 저장할 수있는 의사 코드 일뿐입니다.
다음은 서비스에 대한 의사 코드입니다.
.factory( 'widgetService', function ( $http ) {
function Widget( json ) {
angular.extend( this, json );
}
Widget.prototype = {
$save: function () {
// TODO: strip irrelevant fields
var scrubbedObject = //...
return $http.put( '/widgets/'+this.id, scrubbedObject );
}
};
function getWidgetById ( id ) {
return $http( '/widgets/'+id ).then(function ( json ) {
return new Widget( json );
});
}
// the public widget API
return {
// ...
findById: getWidgetById
// ...
};
});
이 예제에는 포함되지 않았지만 이러한 종류의 유연한 서비스는 상태를 쉽게 관리 할 수 있습니다.
지금 당장은 시간이 없지만 도움이된다면 나중에 간단한 Plunker를 만들어 시연 할 수 있습니다.
답변
어떤 사용 사례를 만족시키려는 지 잘 모르겠습니다. 그러나 객체의 팩토리 반환 인스턴스를 가질 수 있습니다. 필요에 맞게 수정할 수 있어야합니다.
var ExampleApplication = angular.module('ExampleApplication', []);
ExampleApplication.factory('InstancedService', function(){
function Instance(name, type){
this.name = name;
this.type = type;
}
return {
Instance: Instance
}
});
ExampleApplication.controller('InstanceController', function($scope, InstancedService){
var instanceA = new InstancedService.Instance('A','string'),
instanceB = new InstancedService.Instance('B','object');
console.log(angular.equals(instanceA, instanceB));
});
업데이트 됨
비 싱글 톤 서비스에 대한 다음 요청을 고려하십시오 . Brian Ford는 다음과 같이 말합니다.
모든 서비스가 싱글 톤이라는 생각이 새로운 객체를 인스턴스화 할 수있는 싱글 톤 팩토리를 작성하는 것을 막지는 않습니다.
공장에서 인스턴스를 반환하는 그의 예 :
myApp.factory('myService', function () {
var MyThing = function () {};
MyThing.prototype.foo = function () {};
return {
getInstance: function () {
return new MyThing();
}
};
});
나는 또한 new
컨트롤러 에서 키워드 를 사용할 필요가 없기 때문에 그의 예가 우수하다고 주장 합니다. getInstance
서비스 메서드 내에 캡슐화됩니다 .
답변
또 다른 방법은 angular.extend()
.
app.factory('Person', function(){
return {
greet: function() { return "Hello, I'm " + this.name; },
copy: function(name) { return angular.extend({name: name}, this); }
};
});
예를 들어 컨트롤러에서
app.controller('MainCtrl', function ($scope, Person) {
michael = Person.copy('Michael');
peter = Person.copy('Peter');
michael.greet(); // Hello I'm Michael
peter.greet(); // Hello I'm Peter
});
여기에 plunk가 있습니다.
답변
이 게시물에 대한 답변은 이미 받았지만 싱글 톤이 아닌 서비스가 필요한 몇 가지 합법적 인 시나리오가있을 것이라고 생각합니다. 여러 컨트롤러간에 공유 할 수있는 재사용 가능한 비즈니스 로직이 있다고 가정 해 보겠습니다. 이 시나리오에서 로직을 배치하는 가장 좋은 장소는 서비스이지만 재사용 가능한 로직에 일부 상태를 유지해야하는 경우 어떻게해야할까요? 그런 다음 앱의 여러 컨트롤러에서 공유 할 수 있도록 단일 서비스가 아닌 서비스가 필요합니다. 이것이 내가 이러한 서비스를 구현하는 방법입니다.
angular.module('app', [])
.factory('nonSingletonService', function(){
var instance = function (name, type){
this.name = name;
this.type = type;
return this;
}
return instance;
})
.controller('myController', ['$scope', 'nonSingletonService', function($scope, nonSingletonService){
var instanceA = new nonSingletonService('A','string');
var instanceB = new nonSingletonService('B','object');
console.log(angular.equals(instanceA, instanceB));
}]);
답변
다음은 비 싱글 톤 서비스의 예입니다. ORM에서 작업 중입니다. 예제에서 나는 서비스 ( ‘users’, ‘documents’)가 상속하고 잠재적으로 확장하기를 원하는 기본 모델 (ModelFactory)을 보여줍니다.
내 ORM에서 ModelFactory는 모듈 시스템을 사용하여 샌드 박스 화 된 추가 기능 (쿼리, 지속성, 스키마 매핑)을 제공하기 위해 다른 서비스를 주입합니다.
이 예에서 사용자 및 문서 서비스는 모두 동일한 기능을 갖지만 자체 독립 범위를 갖습니다.
/*
A class which which we want to have multiple instances of,
it has two attrs schema, and classname
*/
var ModelFactory;
ModelFactory = function($injector) {
this.schema = {};
this.className = "";
};
Model.prototype.klass = function() {
return {
className: this.className,
schema: this.schema
};
};
Model.prototype.register = function(className, schema) {
this.className = className;
this.schema = schema;
};
angular.module('model', []).factory('ModelFactory', [
'$injector', function($injector) {
return function() {
return $injector.instantiate(ModelFactory);
};
}
]);
/*
Creating multiple instances of ModelFactory
*/
angular.module('models', []).service('userService', [
'ModelFactory', function(modelFactory) {
var instance;
instance = new modelFactory();
instance.register("User", {
name: 'String',
username: 'String',
password: 'String',
email: 'String'
});
return instance;
}
]).service('documentService', [
'ModelFactory', function(modelFactory) {
var instance;
instance = new modelFactory();
instance.register("Document", {
name: 'String',
format: 'String',
fileSize: 'String'
});
return instance;
}
]);
/*
Example Usage
*/
angular.module('controllers', []).controller('exampleController', [
'$scope', 'userService', 'documentService', function($scope, userService, documentService) {
userService.klass();
/*
returns
{
className: "User"
schema: {
name : 'String'
username : 'String'
password: 'String'
email: 'String'
}
}
*/
return documentService.klass();
/*
returns
{
className: "User"
schema: {
name : 'String'
format : 'String'
formatileSize: 'String'
}
}
*/
}
]);
답변
angular는 싱글 톤 서비스 / 공장 옵션 만 제공합니다 . 한 가지 방법은 컨트롤러 또는 기타 소비자 인스턴스 내부에 새 인스턴스를 빌드하는 팩토리 서비스를 사용하는 것입니다. 주입되는 유일한 것은 새 인스턴스를 생성하는 클래스입니다. 다른 종속성을 주입하거나 사용자 사양에 맞게 새 개체를 초기화 할 수있는 좋은 장소입니다 (서비스 또는 구성 추가).
namespace admin.factories {
'use strict';
export interface IModelFactory {
build($log: ng.ILogService, connection: string, collection: string, service: admin.services.ICollectionService): IModel;
}
class ModelFactory implements IModelFactory {
// any injection of services can happen here on the factory constructor...
// I didnt implement a constructor but you can have it contain a $log for example and save the injection from the build funtion.
build($log: ng.ILogService, connection: string, collection: string, service: admin.services.ICollectionService): IModel {
return new Model($log, connection, collection, service);
}
}
export interface IModel {
// query(connection: string, collection: string): ng.IPromise<any>;
}
class Model implements IModel {
constructor(
private $log: ng.ILogService,
private connection: string,
private collection: string,
service: admin.services.ICollectionService) {
};
}
angular.module('admin')
.service('admin.services.ModelFactory', ModelFactory);
}
그런 다음 소비자 인스턴스에서 팩토리 서비스가 필요하고 필요할 때 새 인스턴스를 가져 오기 위해 팩토리에서 빌드 메서드를 호출합니다.
class CollectionController {
public model: admin.factories.IModel;
static $inject = ['$log', '$routeParams', 'admin.services.Collection', 'admin.services.ModelFactory'];
constructor(
private $log: ng.ILogService,
$routeParams: ICollectionParams,
private service: admin.services.ICollectionService,
factory: admin.factories.IModelFactory) {
this.connection = $routeParams.connection;
this.collection = $routeParams.collection;
this.model = factory.build(this.$log, this.connection, this.collection, this.service);
}
}
공장 단계에서 사용할 수없는 일부 특정 서비스를 주입 할 수있는 기회를 제공합니다. 모든 모델 인스턴스에서 사용할 팩토리 인스턴스에서 항상 주입을 수행 할 수 있습니다.
참고 일부 코드를 제거해야 컨텍스트 오류가 발생할 수 있습니다. 작동하는 코드 샘플이 필요하면 알려주세요.
NG2는 DOM의 올바른 위치에 서비스의 새 인스턴스를 삽입 할 수있는 옵션이 있으므로 자체 공장 구현을 구축 할 필요가 없습니다. 기다려야 할 것입니다 🙂
답변
서비스 내에서 개체의 새 인스턴스를 만들어야하는 타당한 이유가 있다고 생각합니다. 우리는 결코 그런 일을하지 말아야한다고 말하는 것보다 열린 마음을 가져야하지만, 싱글 톤은 이유 때문에 그렇게 만들어졌습니다 . 컨트롤러는 앱의 수명주기 내에서 자주 생성 및 삭제되지만 서비스는 영구적이어야합니다.
결제 수락과 같은 일종의 워크 플로가 있고 여러 속성이 설정되어 있지만 고객의 신용 카드가 실패하고 다른 형식을 제공해야하므로 이제 결제 유형을 변경해야하는 사용 사례를 생각할 수 있습니다. 지불. 물론 이것은 앱을 만드는 방법과 많은 관련이 있습니다. 결제 개체의 모든 속성을 재설정하거나 서비스 내에서 개체의 새 인스턴스를 만들 수 있습니다 . 그러나 서비스의 새 인스턴스를 원하지 않거나 페이지를 새로 고치고 싶지 않습니다.
솔루션이 서비스 내에서 새 인스턴스를 만들고 설정할 수있는 개체를 제공한다고 생각합니다. 그러나 명확하게 말하면 컨트롤러가 여러 번 생성되고 파괴 될 수 있지만 서비스에는 지속성이 필요하기 때문에 서비스의 단일 인스턴스가 중요합니다. 찾고있는 것은 Angular 내의 직접적인 메서드가 아니라 서비스 내에서 관리 할 수있는 개체 패턴 일 수 있습니다.
예를 들어 재설정 버튼을 만들었습니다 . (이것은 테스트되지 않았으며 서비스 내에서 새 개체를 만드는 사용 사례에 대한 간단한 아이디어입니다.
app.controller("PaymentController", ['$scope','PaymentService',function($scope, PaymentService) {
$scope.utility = {
reset: PaymentService.payment.reset()
};
}]);
app.factory("PaymentService", ['$http', function ($http) {
var paymentURL = "https://www.paymentserviceprovider.com/servicename/token/"
function PaymentObject(){
// this.user = new User();
/** Credit Card*/
// this.paymentMethod = "";
//...
}
var payment = {
options: ["Cash", "Check", "Existing Credit Card", "New Credit Card"],
paymentMethod: new PaymentObject(),
getService: function(success, fail){
var request = $http({
method: "get",
url: paymentURL
}
);
return ( request.then(success, fail) );
}
//...
}
return {
payment: {
reset: function(){
payment.paymentMethod = new PaymentObject();
},
request: function(success, fail){
return payment.getService(success, fail)
}
}
}
}]);