AngularJS : 디자인 패턴 이해
AngularJS의 책임자 인 Igor Minar가 작성한 이 글 의 맥락에서 :
MVC vs MVVM vs MVP . 많은 개발자들이 토론과 논쟁에 시간과 시간을 할애 할 수있는 논란의 여지가있는 주제입니다.
몇 년 AngularJS와 내용은 가까운 MVC (또는 오히려 그것의 클라이언트 측의 한 변형)에 있지만, 시간과 많은 리팩토링 및 API 개선 덕분에 끝난, 그것은 이제 더 가까이 MVVM - $ 범위 객체는 간주 될 수 있는 ViewModel 되고있는 Controller 라고하는 함수로 장식되었습니다 .
프레임 워크를 분류하여 MV * 버킷 중 하나에 넣을 수 있다는 장점이 있습니다. 프레임 워크를 사용하여 빌드되는 응용 프로그램을 나타내는 멘탈 모델을보다 쉽게 만들 수 있으므로 개발자가 API를보다 편안하게 사용할 수 있습니다. 또한 개발자가 사용하는 용어를 설정하는 데 도움이 될 수 있습니다.
말하지만, 개발자들이 MV * 넌센스에 대해 논쟁하는 데 시간을 낭비하는 것보다 잘 디자인되고 관심사 분리를 따르는 킥-어스 앱을 만드는 것이 오히려 좋습니다. 그리고 이런 이유로 나는 AngularJS 를 MVW 프레임 워크로 선언합니다 – Model-View-Whatever . " 무엇을 위해 당신을 위해 일하는 것 "의 약자 .
Angular는 프리젠 테이션 로직과 비즈니스 로직 및 프리젠 테이션 상태를 멋지게 분리 할 수있는 유연성을 제공합니다. 하루 종일 중요하지 않은 사항에 대한 열띤 토론보다는 생산성과 응용 프로그램 유지 관리 효율성을 높이십시오.
클라이언트 측 애플리케이션에서 AngularJS MVW (Model-View-Whatever) 디자인 패턴을 구현하기위한 권장 사항 또는 지침이 있습니까?
귀중한 많은 소스 덕분에 AngularJS 앱에서 구성 요소를 구현하기위한 일반적인 권장 사항이 있습니다.
제어 장치
컨트롤러는 모델과 뷰 사이의 중간 계층 이어야합니다 . 가능한 한 얇게 만드십시오 .
컨트롤러에서 비즈니스 로직 을 피하는 것이 좋습니다 . 모델로 이동해야합니다.
컨트롤러는 메소드 호출 (자식이 부모와 통신을 원할 때 가능) 또는 $ emit , $ broadcast 및 $ on 메소드를 사용하여 다른 컨트롤러와 통신 할 수 있습니다 . 전송 및 브로드 캐스트 된 메시지는 최소한으로 유지해야합니다.
컨트롤러는 프리젠 테이션 또는 DOM 조작을 신경 쓰지 않아야합니다 .
중첩 된 컨트롤러 를 피 하십시오 . 이 경우 상위 컨트롤러는 모델로 해석됩니다. 대신 공유 서비스로 모델을 주입하십시오.
컨트롤러의 범위 는 프리젠 테이션 모델 디자인 패턴 과 같이 뷰와 모델 을 바인딩 하고 뷰 모델 을
캡슐화 하는 데 사용해야합니다 .
범위
로 처리 범위 읽기 전용 템플릿에 와 쓰기 전용 가능한 컨트롤러 . 범위의 목적은 모델이 아니라 모델을 참조하는 것입니다.
양방향 바인딩 (ng-model)을 수행 할 때는 범위 속성에 직접 바인딩하지 않아야합니다.
모델
AngularJS의 모델은 service에 의해 정의 된 싱글 톤 입니다.
모델은 데이터와 디스플레이를 분리하는 훌륭한 방법을 제공합니다.
모델은 일반적으로 정확히 하나의 종속성 (일부 형태의 이벤트 이미 터 형태, 일반적으로 $ rootScope )을 가지며 테스트 가능한 도메인 로직을 포함하므로 단위 테스트의 주요 후보입니다 .
모델은 특정 단위의 구현으로 간주되어야합니다. 단일 책임 원칙을 기반으로합니다. Unit은 실제 세계에서 단일 엔티티를 나타내고 데이터 및 상태 측면에서 프로그래밍 세계에서이를 설명 할 수있는 관련 논리의 자체 범위를 담당하는 인스턴스입니다 .
모델은 애플리케이션의 데이터를 캡슐화 하고 해당 데이터에 액세스하고 조작 할 수있는 API 를 제공 해야합니다.
모델은 휴대 가 가능 하여 유사한 응용 분야로 쉽게 운반 할 수 있습니다.
모델에서 단위 로직을 분리하면보다 쉽게 찾고 업데이트하고 유지 관리 할 수 있습니다.
모델은 전체 응용 프로그램에 공통적 인보다 일반적인 글로벌 모델의 방법을 사용할 수 있습니다.
구성 요소 결합을 줄이고 단위 테스트 가능성 과 유용성을 높이는 데 의존하지 않는 경우 의존성 주입을 사용하여 모델에 다른 모델의 구성을 피하십시오 .
모델에서 이벤트 리스너를 사용하지 마십시오. 단일 책임 원칙 측면에서 테스트하기가 더 어렵고 일반적으로 모델을 종료합니다.
모델 구현
모델이 데이터 및 상태 측면에서 일부 논리를 캡슐화해야하므로 멤버에 대한 액세스를 구조적으로 제한해야하므로 느슨한 결합을 보장 할 수 있습니다.
AngularJS 애플리케이션에서이를 수행하는 방법은 팩토리 서비스 유형을 사용하여 정의하는 것 입니다. 이를 통해 개인 속성 및 메서드를 매우 쉽게 정의 할 수 있으며 공개적으로 액세스 가능한 항목을 한 곳에서 반환하여 개발자가 실제로 읽을 수 있습니다.
예 :
angular.module('search')
.factory( 'searchModel', ['searchResource', function (searchResource) {
var itemsPerPage = 10,
currentPage = 1,
totalPages = 0,
allLoaded = false,
searchQuery;
function init(params) {
itemsPerPage = params.itemsPerPage || itemsPerPage;
searchQuery = params.substring || searchQuery;
}
function findItems(page, queryParams) {
searchQuery = queryParams.substring || searchQuery;
return searchResource.fetch(searchQuery, page, itemsPerPage).then( function (results) {
totalPages = results.totalPages;
currentPage = results.currentPage;
allLoaded = totalPages <= currentPage;
return results.list
});
}
function findNext() {
return findItems(currentPage + 1);
}
function isAllLoaded() {
return allLoaded;
}
// return public model API
return {
/**
* @param {Object} params
*/
init: init,
/**
* @param {Number} page
* @param {Object} queryParams
* @return {Object} promise
*/
find: findItems,
/**
* @return {Boolean}
*/
allLoaded: isAllLoaded,
/**
* @return {Object} promise
*/
findNext: findNext
};
});
새 인스턴스 만들기
의존성 주입을 중단하기 시작하고 라이브러리가 특히 제 3 자에게 어색하게 작동하므로 새로운 기능을 반환하는 팩토리를 사용하지 마십시오.
동일한 작업을 수행하는 더 좋은 방법은 팩토리를 API로 사용하여 getter 및 setter 메소드가 첨부 된 오브젝트 콜렉션을 리턴하는 것입니다.
angular.module('car')
.factory( 'carModel', ['carResource', function (carResource) {
function Car(data) {
angular.extend(this, data);
}
Car.prototype = {
save: function () {
// TODO: strip irrelevant fields
var carData = //...
return carResource.save(carData);
}
};
function getCarById ( id ) {
return carResource.getById(id).then(function (data) {
return new Car(data);
});
}
// the public API
return {
// ...
findById: getCarById
// ...
};
});
글로벌 모델
일반적으로 이러한 상황을 피하고 모델을 올바르게 설계하여 컨트롤러에 주입하여보기에 사용할 수 있도록하십시오.
특히 일부 방법은 응용 프로그램 내에서 전역 접근성을 필요로합니다. 이를 가능하게하기 위해 $ rootScope 에서 ' common '속성을 정의 하고 애플리케이션 부트 스트랩 동안 commonModel에 바인딩 할 수 있습니다 .
angular.module('app', ['app.common'])
.config(...)
.run(['$rootScope', 'commonModel', function ($rootScope, commonModel) {
$rootScope.common = 'commonModel';
}]);
모든 글로벌 메소드는 ' 공통 '속성 내에 있습니다. 이것은 일종의 네임 스페이스 입니다.
그러나 $ rootScope에 직접 메소드를 정의하지 마십시오 . 이로 인해 뷰 범위 내에서 ngModel 지시문과 함께 사용 하면 예기치 않은 동작이 발생하여 일반적으로 범위가 흩어지고 범위 메서드가 문제를 재정의 할 수 있습니다.
자원
리소스를 사용하면 다른 데이터 소스 와 상호 작용할 수 있습니다 .
single-responsibility-principle을 사용하여 구현해야합니다 .
특히 HTTP / JSON 엔드 포인트에 재사용 가능한 프록시입니다.
모델에 리소스가 주입되어 데이터를 전송 / 검색 할 수 있습니다.
자원 구현
RESTful 서버 측 데이터 소스와 상호 작용할 수있는 자원 오브젝트를 작성하는 팩토리입니다.
리턴 된 자원 오브젝트에는 하위 레벨 $ http 서비스와 상호 작용할 필요없이 상위 레벨 작동을 제공하는 조치 메소드가 있습니다.
서비스
모델과 리소스는 모두 서비스 입니다.
서비스는 독립적 이며 느슨하게 결합 된 독립적 인 기능 단위입니다.
서비스는 Angular가 서버 측에서 클라이언트 측 웹 앱으로 가져 오는 기능으로, 오랫동안 서비스가 일반적으로 사용되었습니다.
Angular 앱의 서비스는 의존성 주입을 사용하여 서로 연결된 대체 가능한 객체입니다.
Angular에는 다양한 유형의 서비스가 제공됩니다. 각각 자체 사용 사례가 있습니다. 자세한 내용은 서비스 유형 이해를 읽으십시오 .
애플리케이션에서 서비스 아키텍처의 기본 원칙 을 고려 하십시오.
일반적으로 웹 서비스 용어 에 따르면 :
서비스는 제공자 엔티티 및 요청자 엔티티의 관점에서 일관된 기능을 형성하는 태스크를 수행하는 기능을 나타내는 추상 자원입니다. 사용하려면 구체적인 제공자 에이전트가 서비스를 실현해야합니다.
클라이언트 측 구조
일반적으로 응용 프로그램의 클라이언트 쪽은 모듈 로 분할됩니다 . 각 모듈은 하나의 단위 로 테스트 할 수 있어야합니다 .
유형이 아닌 기능 / 뷰 또는 기능 에 따라 모듈을 정의하십시오 . 자세한 내용은 Misko의 프레젠테이션 을 참조하십시오.
모듈 구성 요소는 일반적으로 컨트롤러, 모델, 뷰, 필터, 지시문 등과 같은 유형별로 그룹화 될 수 있습니다.
그러나 모듈 자체는 재사용 가능 하고 전송 가능 하며 테스트 가능 합니다.
개발자가 코드의 일부와 모든 종속 항목을 찾는 것이 훨씬 쉽습니다.
자세한 내용 은 Large AngularJS 및 JavaScript 애플리케이션의 코드 구성을 참조 하십시오.
폴더 구조의 예 :
|-- src/
| |-- app/
| | |-- app.js
| | |-- home/
| | | |-- home.js
| | | |-- homeCtrl.js
| | | |-- home.spec.js
| | | |-- home.tpl.html
| | | |-- home.less
| | |-- user/
| | | |-- user.js
| | | |-- userCtrl.js
| | | |-- userModel.js
| | | |-- userResource.js
| | | |-- user.spec.js
| | | |-- user.tpl.html
| | | |-- user.less
| | | |-- create/
| | | | |-- create.js
| | | | |-- createCtrl.js
| | | | |-- create.tpl.html
| |-- common/
| | |-- authentication/
| | | |-- authentication.js
| | | |-- authenticationModel.js
| | | |-- authenticationService.js
| |-- assets/
| | |-- images/
| | | |-- logo.png
| | | |-- user/
| | | | |-- user-icon.png
| | | | |-- user-default-avatar.png
| |-- index.html
각도 응용 프로그램 구조화의 좋은 예는 angular -app - https : //github.com/angular-app/angular-app/tree/master/client/src에 의해 구현됩니다.
이것은 현대 응용 프로그램 생성기에서도 고려됩니다-https: //github.com/yeoman/generator-angular/issues/109
나는 당신이 제공 한 인용에서 볼 수 있듯이 Igor가 이것을 받아들이는 것이 훨씬 더 큰 문제의 빙산의 일각이라고 생각합니다.
MVC 와 그 파생물 (MVP, PM, MVVM)은 단일 에이전트 내에서 모두 훌륭하고 멋지지만 서버-클라이언트 아키텍처는 모든 목적을 위해 2- 에이전트 시스템이며 사람들은 종종 이러한 패턴에 집착하여 당면한 문제는 훨씬 더 복잡합니다. 이러한 원칙을 준수하려고 시도하면 실제로 결함이있는 아키텍처가됩니다.
이것을 조금씩 해봅시다.
지침
견해
각도 컨텍스트 내에서보기는 DOM입니다. 지침은 다음과 같습니다.
하다:
- 현재 범위 변수 (읽기 전용)
- 컨트롤러에 조치를 요청하십시오.
하지 마십시오 :
- 어떤 논리라도 넣으십시오.
유혹적이고 짧고 무해한 것처럼 보입니다.
ng-click="collapsed = !collapsed"
Javascript 파일과 HTML 파일을 모두 검사하는 데 필요한 시스템 작동 방식을 이해하려면 모든 개발자에게 의미가 있습니다.
컨트롤러
하다:
- 범위에 데이터를 배치하여 뷰를 '모델'에 바인딩하십시오.
- 사용자 조치에 응답하십시오.
- 프리젠 테이션 로직 다루기.
하지 마십시오 :
- 모든 비즈니스 로직을 다루십시오.
마지막 지침의 이유는 컨트롤러가 엔티티가 아닌 뷰의 자매이기 때문입니다. 재사용이 불가능합니다.
지시문을 재사용 할 수 있다고 주장 할 수 있지만 지시문도 뷰에 대한 자매 (DOM)입니다.이 지시문은 엔티티에 해당하지 않습니다.
물론 뷰는 개체를 나타내지 만 다소 구체적인 경우입니다.
다시 말해, 컨트롤러는 프리젠 테이션에 초점을 두어야합니다. 비즈니스 로직을 도입하면 부풀어지고 관리하기 어려운 컨트롤러가 생길뿐만 아니라 우려 분리 원칙을 위반하는 것입니다.
따라서 Angular의 컨트롤러는 실제로 Presentation Model 또는 MVVM에 가깝습니다 .
컨트롤러가 비즈니스 로직을 다루지 않으면 누가해야합니까?
모델이란 무엇입니까?
고객 모델은 종종 부분적이고 부실합니다
오프라인 웹 응용 프로그램이나 매우 간단한 응용 프로그램 (엔티티가 거의 없음)을 작성하지 않는 한 클라이언트 모델은 다음과 같습니다.
- 부분
- 페이지 매김의 경우와 같이 모든 엔터티를 가지고 있지는 않습니다.
- 또는 페이지 매김의 경우와 같이 모든 데이터가 없습니다
- 부실 -시스템에 둘 이상의 사용자가있는 경우 언제든지 클라이언트가 보유한 모델이 서버가 보유한 모델과 동일한 지 확인할 수 없습니다.
실제 모델은 지속되어야합니다
기존 MCV에서는이 모델 만 유지 됩니다. 우리가 모델에 관해 이야기 할 때마다, 그것들은 어느 시점에서 지속되어야합니다. 클라이언트는 임의로 모델을 조작 할 수 있지만 서버 왕복이 성공적으로 완료 될 때까지 작업이 완료되지 않습니다.
결과
위의 두 가지 사항은주의해야합니다. 고객이 보유한 모델에는 부분적이고 간단한 비즈니스 로직 만 포함될 수 있습니다.
따라서, 그것은 사용 소문자로, 클라이언트의 컨텍스트 내에서, 아마도 현명하다 M
- 정말 그래서 MVC , MVP 및 MVVM을 . 큰 M
것은 서버입니다.
비즈니스 로직
아마도 비즈니스 모델에 대한 가장 중요한 개념 중 하나는 두 가지 유형으로 세분화 할 수 있다는 것입니다 (제 3의 뷰 비즈니스 는 다른 날의 이야기이므로 생략합니다 ).
- 도메인 로직 -일명 엔터프라이즈 비즈니스 규칙 - 애플리케이션 독립적 인 로직. 예를 들어, 모델에
firstName
및sirName
속성을 지정하면 getter와 같은getFullName()
애플리케이션 독립형으로 간주 될 수 있습니다. - 응용 프로그램 논리 - 응용 프로그램 별 규칙 인 응용 프로그램 비즈니스 규칙 . 예를 들어, 오류 점검 및 처리.
클라이언트 컨텍스트 내에서이 두 가지 모두 '실제'비즈니스 로직 이 아니라는 점을 강조하는 것이 중요합니다. 클라이언트의 중요한 부분 만 처리합니다. 응용 프로그램 논리 (도메인 논리가 아님)는 서버와의 통신 및 대부분의 사용자 상호 작용을 촉진해야합니다. 도메인 로직은 주로 소규모, 엔티티 별 및 프리젠 테이션 중심입니다.
문제는 여전히 남아 있습니다-각도 응용 프로그램 내에서 어디로 던지십니까?
3 대 4 레이어 아키텍처
이 모든 MVW 프레임 워크는 3 개의 레이어를 사용합니다.
그러나 클라이언트와 관련하여 두 가지 근본적인 문제가 있습니다.
- 모델이 부분적이고 부실하며 지속되지 않습니다.
- 애플리케이션 로직을 넣을 곳이 없습니다.
이 전략의 대안은 4 계층 전략입니다 .
The real deal here is the application business rules layer (Use cases), which often goes amiss on clients.
This layer is realised by interactors (Uncle Bob), which is pretty much what Martin Fowler calls an operation script service layer.
Concrete example
Consider the following web application:
- The application shows a paginated list of users.
- The user clicks 'Add user'.
- A model opens with a form to fill user details.
- The user fills the form and hit submit.
A few things should happen now:
- The form should be client-validated.
- A request shall be sent to the server.
- An error shall be handled, if there is one.
- The user list may or may not (due to pagination) needs updating.
Where do we throw all of this?
If your architecture involves a controller that calls $resource
, all of this will happen within the controller. But there is a better strategy.
A proposed solution
The following diagram shows how the problem above can be solve by adding another application logic layer in Angular clients:
So we add a layer between controller to $resource, this layer (lets call it interactor):
- Is a service. In the case of users, it may be called
UserInteractor
. - It provide methods corresponding to use cases, encapsulating application logic.
- It controls the requests made to the server. Instead of a controller calling $resource with free-form parameters, this layer ensure that requests made to the server return data on which domain logic can act.
- It decorates the returned data structure with domain logic prototype.
And so, with the requirements of the concrete example above:
- The user clicks 'Add user'.
- The controller asks the interactor for a blank user model, the is decorated with business logic method, like
validate()
- Upon submission, the controller calls the model
validate()
method. - If failed, the controller handles the error.
- If successful, the controller calls the interactor with
createUser()
- The interactor calls $resource
- Upon response, the interactor delegates any errors to the controller, which handles them.
- Upon successful response, the interactor ensures that if needed, the user list updates.
A minor issue comparing to the great advices in Artem's answer, but in terms of code readability, I found best to define the API completely inside the return
object, to minimize going back and forth in code to look wheverer variables are defined:
angular.module('myModule', [])
// or .constant instead of .value
.value('myConfig', {
var1: value1,
var2: value2
...
})
.factory('myFactory', function(myConfig) {
...preliminary work with myConfig...
return {
// comments
myAPIproperty1: ...,
...
myAPImethod1: function(arg1, ...) {
...
}
}
});
If the return
object becomes looking "too crowded", that is a sign that the Service is doing too much.
AngularJS doest not implement MVC in traditional way, rather it implements something closer to MVVM(Model-View-ViewModel), ViewModel can also be referred as binder(in angular case it can be $scope). The Model--> As we know model in angular can be just plain old JS objects or the data in our application
The View--> the view in angularJS is the HTML which has been parsed and compiled by angularJS by applying the directives or instructions or bindings, Main point here is in angular the input is not just the plain HTML string(innerHTML), rather it is DOM created by browser.
The ViewModel--> ViewModel is actually the binder/bridge between your view and model in angularJS case it is $scope, to initialize and augment the $scope we use Controller.
If i wanna summarize the answer: In angularJS application $scope has reference to the data, Controller controls the behaviour, and View handles the layout by interacting with controller to behave accordingly.
To be crisp about the question, Angular uses different design patterns which we already encountered in our regular programming. 1) When we registers our controllers or directives, factory, services etc with respect to our module. Here it is hiding the data from the global space. Which is Module pattern. 2) When angular uses its dirty checking for comparing the scope variables, here it uses Observer Pattern. 3) All the parent child scopes in our controllers uses Prototypal pattern. 4) In case of injecting the services it uses Factory Pattern.
Overall it uses different known design patterns to solve the problems.
참고URL : https://stackoverflow.com/questions/20286917/angularjs-understanding-design-pattern
'development' 카테고리의 다른 글
왜 int 변수 i가 아닌 short로 1을 전달할 수 있습니까? (0) | 2020.06.14 |
---|---|
캐스퍼 즈 / 팬텀 제이스 vs 셀레늄 (0) | 2020.06.14 |
SQL 쿼리를 사용하여 문자열을 int로 변환 (0) | 2020.06.14 |
C에서 배열을 0으로 초기화하는 방법은 무엇입니까? (0) | 2020.06.14 |
Android에서 RxJava를 사용하는시기와 Android Architectural Components에서 LiveData를 사용하는시기는 언제입니까? (0) | 2020.06.14 |