เชิงมุมเทียบกับเชิงมุมโรงงาน


1065

ฉันเห็นทั้งangular.factory ()และangular.service ()เคยประกาศบริการ อย่างไรก็ตามฉันไม่พบ angular.serviceที่ใดก็ได้ในเอกสารอย่างเป็นทางการ

ความแตกต่างระหว่างสองวิธีคืออะไร?
ควรใช้สิ่งใด (สมมติว่าพวกเขาทำสิ่งต่าง ๆ )



4
ฉันค้นหา "[angularjs] service service" แต่ฉันยังจำได้ว่ามีคำถามเกี่ยวกับเรื่องนี้อยู่แล้ว (เพราะฉันคิดเกี่ยวกับการเขียนที่ / คำถามนี้เอง ณ จุดหนึ่ง)
Mark Rajcok

2
ในการค้นหาให้ทำเครื่องหมายวงเล็บเหลี่ยมว่าแท็กหรือไม่
jacob

11
@Jacob Square Brackets กำลัง จำกัด การค้นหาของคุณให้แคบลง [angularjs] directives - จะค้นหา 'directives' สำหรับคำถามที่ติดแท็กแล้วด้วย angularjs
Mahbub

1
@Mahbub ในคำอื่น ๆ "ใช่" :)
ไบรอัน

คำตอบ:


1268
  angular.service('myService', myServiceFunction);
  angular.factory('myFactory', myFactoryFunction);

ฉันมีปัญหาห่อหัวของฉันรอบแนวคิดนี้จนกว่าฉันจะทำให้ตัวเองด้วยวิธีนี้:

บริการ : ฟังก์ชั่นที่คุณเขียนจะใหม่ -ed:

  myInjectedService  <----  new myServiceFunction()

Factory : ฟังก์ชั่น (นวกรรมิก) ที่คุณเขียนจะถูกเรียกใช้ :

  myInjectedFactory  <---  myFactoryFunction()

สิ่งที่คุณทำขึ้นอยู่กับคุณ แต่มีบางรูปแบบที่มีประโยชน์ ...

เช่นการเขียนฟังก์ชั่นบริการเพื่อเปิดเผย API สาธารณะ:

function myServiceFunction() {
  this.awesomeApi = function(optional) {
    // calculate some stuff
    return awesomeListOfValues;
  }
}
---------------------------------------------------------------------------------
// Injected in your controller
$scope.awesome = myInjectedService.awesomeApi();

หรือใช้ฟังก์ชั่นจากโรงงานเพื่อเปิดเผย API สาธารณะ:

function myFactoryFunction() {
  var aPrivateVariable = "yay";

  function hello() {
    return "hello mars " + aPrivateVariable;
  }

  // expose a public API
  return {
    hello: hello
  };
}
---------------------------------------------------------------------------------
// Injected in your controller
$scope.hello = myInjectedFactory.hello();

หรือใช้ฟังก์ชั่นจากโรงงานเพื่อคืนค่าคอนสตรัคเตอร์:

function myFactoryFunction() {
    return function() {
        var a = 2;
        this.a2 = function() {
            return a*2;
        };
    };
}
---------------------------------------------------------------------------------
// Injected in your controller
var myShinyNewObject = new myInjectedFactory();
$scope.four = myShinyNewObject.a2();

จะใช้อันไหนดี ...

คุณสามารถทำสิ่งเดียวกันทั้งสองอย่างได้ อย่างไรก็ตามในบางกรณีโรงงานให้ความยืดหยุ่นเล็กน้อยในการสร้างการฉีดด้วยไวยากรณ์ที่ง่ายขึ้น นั่นเป็นเพราะในขณะที่ myInjectedService ต้องเป็นวัตถุเสมอ myInjectedFactory สามารถเป็นวัตถุการอ้างอิงฟังก์ชันหรือค่าใด ๆ ก็ได้ ตัวอย่างเช่นถ้าคุณเขียนบริการเพื่อสร้างนวกรรมิก (ดังในตัวอย่างด้านบน) มันจะต้องมีการยกตัวอย่างเช่น:

var myShinyNewObject = new myInjectedService.myFunction()

ซึ่งเนื้อหาที่ต้องการน้อยกว่านี้:

var myShinyNewObject = new myInjectedFactory();

(แต่คุณควรระวังเกี่ยวกับการใช้รูปแบบประเภทนี้ตั้งแต่แรกเพราะวัตถุที่สร้างขึ้นใหม่ในคอนโทรลเลอร์ของคุณสร้างการอ้างอิงที่ยากต่อการติดตามที่ยากต่อการจำลองการทดสอบดีกว่าที่จะให้บริการจัดการชุดของวัตถุสำหรับ คุณมากกว่าใช้new()wily-nilly)


อีกอย่างพวกเขาล้วนเป็นโสด ...

โปรดทราบว่าในทั้งสองกรณีแองกูลาร์กำลังช่วยคุณจัดการซิงเกิลตัน ไม่ว่าคุณจะฉีดบริการหรือฟังก์ชั่นของคุณที่ไหนหรือกี่ครั้งคุณจะได้รับการอ้างอิงเดียวกันกับวัตถุหรือฟังก์ชั่นเดียวกัน (ยกเว้นเมื่อโรงงานส่งคืนค่าเช่นตัวเลขหรือสตริงในกรณีนั้นคุณจะได้รับค่าเดิมเสมอ แต่ไม่ใช่การอ้างอิง)


2
มันจะดีกว่าไหมถ้าเรียกมันว่าเป็นตัวสร้างวัตถุมากกว่า Newable
markyzm

2
@Hugo ฉันแสดงให้เห็นว่าคุณสามารถทำสิ่งเดียวกันทั้งสองได้อย่างมีประสิทธิภาพเพียงแค่ว่าไวยากรณ์จะแตกต่างกัน
Gil Birman

105
ฉันไม่แน่ใจว่าจะต้องอ่านกี่ครั้งเกี่ยวกับความแตกต่างระหว่างการบริการและโรงงานก่อนที่ฉันจะมั่นใจว่าทั้งสองอย่างนั้นมีความจำเป็น
DMac the Destroyer

10
เรามีคำกริยาที่จะพูดว่า "ใหม่" มันเป็น "อินสแตนซ์" เพียงเพื่อการอ้างอิง :)
sscarduzio

7
โรงงานคือฟังก์ชั่นที่ถูกเรียกใช้เพื่อให้พวกเขาสามารถคืนสิ่งใดก็ได้ ในทางกลับกันการบริการจะถูกทำให้เป็นมุมโดยผ่านทางnew fn()ดังนั้นพวกเขาจะต้องส่งคืน
Gil Birman

318

ใส่เพียง ..

// Service
service = (a, b) => {
  a.lastName = b;
  return a;
};

// Factory
factory = (a, b) => Object.assign({}, a, { lastName: b });

const fullName = { firstName: 'john' };

// Service
const lastNameService = (a, b) => {
  a.lastName = b;
  return a;
};
console.log(lastNameService(fullName, 'doe'));

// Factory
const lastNameFactory = (a, b) => 
  Object.assign({}, a, { lastName: b })
console.log(lastNameFactory(fullName, 'doe'));


169
เพื่อนขอบคุณ ไม่ใช่ว่ารายละเอียดของคำตอบอื่น ๆ นั้นไม่ถูกต้อง แต่บางครั้งคุณต้องการรุ่น 10 วินาที
R Claven

4
เพียงแค่ให้ฟังก์ชั่นบริการไม่มีอะไรคืน this.name = ...ก็เพียงพอที่จะแสดงให้เห็นว่ามันเป็นที่การเปิดเผย API
pixelbits

3
อย่างไรก็ตามถ้าคุณส่งคืนและคัดค้านมันจะใช้สิ่งนั้นแทนสิ่งนี้ jsfiddle.net/Ne5P8/1221
MrB

@MrB นั่นเป็นคุณสมบัติ JavaScript ปกติไม่ใช่เฉพาะกับ Angular หรือบริบทของคำถามนี้
Om Shankar

@Om Shankar คำตอบข้างต้นแสดงให้เห็นว่าความแตกต่างคือการใช้ vs นี้กับวัตถุที่ส่งคืน ฉันแสดงว่า "นี่" เป็นค่าเริ่มต้นที่จะใช้กับบริการอย่างไรก็ตามถ้าคุณส่งคืนค่ามันจะทำหน้าที่เหมือนโรงงาน อย่างไรก็ตามในทางกลับกันโรงงานดูเหมือนจะต้องการค่าที่ส่งคืนมิฉะนั้นจะเกิดข้อผิดพลาด - (แสดงในตัวอย่างนี้ - jsfiddle.net/hmoc0q3v/1 )
MrB

247

นี่คือความแตกต่างหลัก:

บริการ

ไวยากรณ์: module.service( 'serviceName', function );

ส่งผลให้เกิด: เมื่อประกาศ SERVICENAME เป็นอาร์กิวเมนต์ฉีดคุณจะมีให้กับอินสแตนซ์ของฟังก์ชันmodule.serviceส่งผ่านไปยัง

การใช้งาน: อาจมีประโยชน์สำหรับการแบ่งปันฟังก์ชั่นยูทิลิตี้ที่มีประโยชน์ในการเรียกใช้โดยการต่อท้าย( )การอ้างอิงฟังก์ชั่นการฉีด อาจจะทำงานด้วยinjectedArg.call( this )หรือคล้ายกัน

โรงงาน

ไวยากรณ์: module.factory( 'factoryName', function );

ผล: เมื่อประกาศ factoryName เป็นอาร์กิวเมนต์ฉีดคุณจะได้รับกับค่าที่ถูกส่งกลับโดยเรียกอ้างอิงฟังก์ชันmodule.factoryส่งผ่านไปยัง

การใช้งาน: อาจมีประโยชน์สำหรับการส่งคืนฟังก์ชัน'คลาส'ที่สามารถเป็น new'ed เพื่อสร้างอินสแตนซ์

นี่คือตัวอย่างการใช้บริการและโรงงาน อ่านเพิ่มเติมเกี่ยวกับAngularJS บริการ VS โรงงาน

นอกจากนี้คุณยังสามารถตรวจสอบเอกสาร AngularJSและคำถามที่คล้ายกันใน StackOverflow สับสนเกี่ยวกับการบริการเทียบกับโรงงาน


27
ฉันไม่เห็นด้วยกับตัวอย่างการใช้งานของโรงงาน ทั้งบริการและโรงงาน (สมมติว่าฟังก์ชั่นจะถูกส่งกลับอาจเป็นเพียงค่าหรือวัตถุ) ที่สามารถ newed ในความเป็นจริงบริการเป็นตัวเลือกเดียวที่รับประกันว่าจะเป็น new'able เนื่องจากคุณได้รับอินสแตนซ์ของฟังก์ชัน ฉันจะบอกว่าประโยชน์ของการใช้โรงงานมากกว่าบริการคือมันช่วยให้การควบคุมการเข้าถึงคุณสมบัติ - ส่วนตัว & สาธารณะต่อ se ในขณะที่คุณสมบัติทั้งหมดของบริการโดยธรรมชาติเปิดเผย และฉันคิดว่าผู้ให้บริการเป็นโรงงานของโรงงาน - มีเพียงการฉีดและกำหนดค่าได้ในเวลากำหนดค่า
Drew R

1
@DrewR ขอบคุณสำหรับความคิดเห็นของคุณฉันพบตัวอย่างที่ดีของวิธีการของรัฐและเอกชนโดยใช้โรงงาน: stackoverflow.com/a/14904891/65025
edzillion

ฉันต้องเห็นด้วยกับ @DrewR ในอันนี้จริง ๆ ฉันเคยใช้โรงงานเพื่อส่งคืนวัตถุมาก่อน แต่จริงๆแล้ว ณ จุดนี้มันอาจคุ้มค่าที่จะใช้งาน$providersตลอดเวลา
jedd.ahyoung

บริการเป็นตัวสร้างอินสแตนซ์อัตโนมัติใช่มั้ย
Martian2049

1
@DrewR - จากความเข้าใจของฉันมันเป็นความจริงที่คุณสามารถรับเอฟเฟ็กต์ใหม่ ๆ ที่เหมือนกันจากการบริการเช่นเดียวกับที่คุณสามารถทำได้จากโรงงาน เป้าหมายหลักคือเมื่อคุณต้องการส่งคืนวัตถุยูทิลิตี้บางอย่างและเพื่อให้มีไวยากรณ์ที่เหมาะสมยิ่งขึ้น - คุณสามารถเขียนthis.myFunc = function(){}ในบริการของคุณ (ช่วยให้คุณประหยัดจากการเขียนรหัสเพื่อสร้างวัตถุเหมือนที่คุณต้องทำกับโรงงาน )
BornToCode

137

TL; DR

1)เมื่อคุณใช้Factory ที่คุณสร้างวัตถุให้เพิ่มคุณสมบัติเข้าไปจากนั้นส่งคืนวัตถุเดียวกันนั้น เมื่อคุณส่งโรงงานนี้ไปยังตัวควบคุมของคุณคุณสมบัติเหล่านั้นบนวัตถุจะมีอยู่ในตัวควบคุมนั้นผ่านโรงงานของคุณ

app.controller('myFactoryCtrl', function($scope, myFactory){
  $scope.artist = myFactory.getArtist();
});

app.factory('myFactory', function(){
  var _artist = 'Shakira';
  var service = {};

  service.getArtist = function(){
    return _artist;
  }

  return service;
});


2)เมื่อคุณใช้บริการ Angular จะยกตัวอย่างเบื้องหลังด้วยคำหลัก 'ใหม่' ด้วยเหตุนี้คุณจะเพิ่มคุณสมบัติให้กับ 'this' และบริการจะส่งคืน 'this' เมื่อคุณส่งผ่านบริการไปยังคอนโทรลเลอร์ของคุณคุณสมบัติเหล่านี้ใน 'this' จะพร้อมใช้งานบนคอนโทรลเลอร์ดังกล่าวผ่านบริการของคุณ

app.controller('myServiceCtrl', function($scope, myService){
  $scope.artist = myService.getArtist();
});

app.service('myService', function(){
  var _artist = 'Nelly';
  this.getArtist = function(){
    return _artist;
  }
});



ไม่ใช่ TL; DR

1) Factory Factory
เป็นวิธีที่นิยมที่สุดในการสร้างและกำหนดค่าบริการ ไม่มีอะไรมากไปกว่าสิ่งที่ TL; DR กล่าว คุณเพิ่งสร้างวัตถุเพิ่มคุณสมบัติเข้าไปแล้วส่งคืนวัตถุเดียวกันนั้น จากนั้นเมื่อคุณส่งโรงงานไปยังคอนโทรลเลอร์ของคุณคุณสมบัติเหล่านั้นบนวัตถุจะมีอยู่ในคอนโทรลเลอร์นั้นผ่านโรงงานของคุณ ตัวอย่างที่กว้างขวางยิ่งขึ้นอยู่ด้านล่าง

app.factory('myFactory', function(){
  var service = {};
  return service;
});

ตอนนี้คุณสมบัติอะไรก็ตามที่เราแนบกับ 'บริการ' จะมีให้เราเมื่อเราส่ง 'myFactory' เข้าไปในตัวควบคุมของเรา

ตอนนี้เราจะเพิ่มตัวแปร 'ส่วนตัว' ลงในฟังก์ชันการโทรกลับของเรา สิ่งเหล่านี้จะไม่สามารถเข้าถึงได้โดยตรงจากคอนโทรลเลอร์ แต่ในที่สุดเราจะตั้งค่าเมธอด getter / setter ใน 'บริการ' เพื่อให้สามารถเปลี่ยนตัวแปร 'ส่วนตัว' เหล่านี้เมื่อจำเป็น

app.factory('myFactory', function($http, $q){
  var service = {};
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  var makeUrl = function(){
   _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK';
    return _finalUrl
  }

  return service;
});

ที่นี่คุณจะสังเกตเห็นว่าเราไม่ได้แนบตัวแปร / ฟังก์ชั่นเหล่านั้นเข้ากับ 'บริการ' เราแค่สร้างมันขึ้นมาเพื่อใช้หรือดัดแปลงในภายหลัง

  • baseUrl เป็น URL หลักที่ iTunes API ต้องการ
  • _artist เป็นศิลปินที่เราต้องการค้นหา
  • _finalUrl เป็น URL สุดท้ายและสร้างขึ้นอย่างเต็มที่ซึ่งเราจะทำการเรียกไปยัง iTunes makeUrl เป็นฟังก์ชันที่จะสร้างและส่งคืน URL ที่เป็นมิตรกับ iTunes ของเรา

ตอนนี้ผู้ช่วย / ตัวแปรส่วนตัวและฟังก์ชั่นของเราพร้อมใช้งานแล้วให้เพิ่มคุณสมบัติบางอย่างลงในออบเจ็กต์ 'บริการ' สิ่งที่เราใส่ใน 'บริการ' เราจะสามารถใช้โดยตรงในตัวควบคุมใดก็ตามที่เราส่งผ่าน 'myFactory' เข้าไป

เราจะสร้างเมธอด setArtist และ getArtist ที่เพียงแค่คืนค่าหรือตั้งค่าศิลปิน เรากำลังจะสร้างวิธีการที่จะเรียก iTunes API ด้วย URL ที่สร้างขึ้นของเรา วิธีนี้จะคืนสัญญาที่จะตอบสนองเมื่อข้อมูลได้กลับมาจาก iTunes API หากคุณยังไม่เคยมีประสบการณ์การใช้คำสัญญาใน Angular มากนักฉันขอแนะนำให้ดำน้ำลึก

ด้านล่างsetArtistยอมรับศิลปินและอนุญาตให้คุณตั้งค่าศิลปิน getArtistส่งคืนcall callItunesของศิลปินสายแรก makeUrl () เพื่อสร้าง URL ที่เราจะใช้กับคำขอ $ http ของเรา จากนั้นจะตั้งค่าวัตถุสัญญาทำให้คำขอ $ http เป็น URL สุดท้ายของเราจากนั้นเนื่องจาก $ http ส่งคืนสัญญาเราจึงสามารถโทรหา. สำเร็จหรือ .error หลังจากคำขอของเรา จากนั้นเราจะแก้ไขสัญญาของเรากับข้อมูล iTunes หรือเราปฏิเสธด้วยข้อความแจ้งว่า 'มีข้อผิดพลาด'

app.factory('myFactory', function($http, $q){
  var service = {};
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }

  service.setArtist = function(artist){
    _artist = artist;
  }

  service.getArtist = function(){
    return _artist;
  }

  service.callItunes = function(){
    makeUrl();
    var deferred = $q.defer();
    $http({
      method: 'JSONP',
      url: _finalUrl
    }).success(function(data){
      deferred.resolve(data);
    }).error(function(){
      deferred.reject('There was an error')
    })
    return deferred.promise;
  }

  return service;
});

ตอนนี้โรงงานของเราเสร็จสมบูรณ์แล้ว ตอนนี้เราสามารถฉีด 'myFactory' ลงในตัวควบคุมใด ๆ แล้วเราจะสามารถเรียกวิธีการของเราที่เราแนบไปกับวัตถุบริการของเรา (setArtist, getArtist และ callItunes)

app.controller('myFactoryCtrl', function($scope, myFactory){
  $scope.data = {};
  $scope.updateArtist = function(){
    myFactory.setArtist($scope.data.artist);
  };

  $scope.submitArtist = function(){
    myFactory.callItunes()
      .then(function(data){
        $scope.data.artistData = data;
      }, function(data){
        alert(data);
      })
  }
});

ในตัวควบคุมด้านบนเรากำลังทำการฉีดในบริการ 'myFactory' จากนั้นเราตั้งค่าคุณสมบัติบนวัตถุ $ scope ที่มาจากข้อมูลจาก 'myFactory' โค้ดที่ยุ่งยากเพียงอย่างเดียวด้านบนคือหากคุณไม่เคยทำสัญญากับคุณมาก่อน เนื่องจาก callItunes ส่งคืนสัญญาเราจึงสามารถใช้วิธีการ. then () และตั้งค่า $ scope.data.artistData เพียงครั้งเดียวเมื่อสัญญาของเราได้รับการปฏิบัติตามข้อมูล iTunes คุณจะสังเกตเห็นว่าคอนโทรลเลอร์ของเรานั้น 'บาง' มาก ตรรกะและข้อมูลถาวรทั้งหมดของเราตั้งอยู่ในบริการของเราไม่ใช่ในตัวควบคุมของเรา

2) บริการ
บางทีสิ่งที่สำคัญที่สุดที่ควรทราบเมื่อต้องจัดการกับการสร้างบริการคือการสร้างอินสแตนซ์ของคำหลัก 'ใหม่' สำหรับคุณ gurus JavaScript คุณควรให้คำแนะนำอย่างละเอียดเกี่ยวกับลักษณะของรหัส สำหรับผู้ที่มีพื้นหลังที่ จำกัด ใน JavaScript หรือสำหรับผู้ที่ไม่คุ้นเคยกับสิ่งที่คำหลัก 'ใหม่' จริงลองมาตรวจทานพื้นฐาน JavaScript ที่จะช่วยเราในการทำความเข้าใจลักษณะของบริการ

หากต้องการดูการเปลี่ยนแปลงที่เกิดขึ้นเมื่อคุณเรียกใช้ฟังก์ชันด้วยคำหลัก 'ใหม่' ให้สร้างฟังก์ชันและเรียกใช้ด้วยคำหลัก 'ใหม่' จากนั้นให้แสดงสิ่งที่ล่ามทำเมื่อเห็นคำหลัก 'ใหม่' ผลลัพธ์ที่ได้จะเหมือนกันทั้งคู่

ก่อนอื่นมาสร้างตัวสร้างของเรา

var Person = function(name, age){
  this.name = name;
  this.age = age;
}

นี่คือฟังก์ชันตัวสร้าง JavaScript ทั่วไป ตอนนี้เมื่อใดก็ตามที่เราเรียกใช้ฟังก์ชั่นบุคคลโดยใช้คำหลัก 'ใหม่' นี้จะถูกผูกไว้กับวัตถุที่สร้างขึ้นใหม่

ตอนนี้เราจะเพิ่มวิธีการลงบนต้นแบบของบุคคลของเราเพื่อให้สามารถใช้ได้กับทุก ๆ ตัวอย่างของ 'ชั้นเรียน' ของบุคคล

Person.prototype.sayName = function(){
  alert('My name is ' + this.name);
}

ตอนนี้เนื่องจากเราใส่ฟังก์ชั่น sayName ลงบนต้นแบบทุกอินสแตนซ์ของ Person จะสามารถเรียกใช้ฟังก์ชัน sayName เพื่อแจ้งเตือนการสั่งซื้อชื่อของอินสแตนซ์นั้น

ตอนนี้เรามีฟังก์ชั่นตัวสร้างบุคคลของเราและฟังก์ชั่น sayName ของเราบนต้นแบบแล้วลองสร้างตัวอย่างของบุคคลจากนั้นเรียกใช้ฟังก์ชั่น sayName

var tyler = new Person('Tyler', 23);
tyler.sayName(); //alerts 'My name is Tyler'

ดังนั้นทุกรหัสเข้าด้วยกันสำหรับการสร้างตัวสร้างบุคคลเพิ่มฟังก์ชั่นของมันเป็นต้นแบบสร้างอินสแตนซ์ของบุคคลแล้วเรียกฟังก์ชั่นบนต้นแบบของมันมีลักษณะเช่นนี้

var Person = function(name, age){
  this.name = name;
  this.age = age;
}
Person.prototype.sayName = function(){
  alert('My name is ' + this.name);
}
var tyler = new Person('Tyler', 23);
tyler.sayName(); //alerts 'My name is Tyler'

ตอนนี้เรามาดูสิ่งที่เกิดขึ้นจริงเมื่อคุณใช้คำหลัก 'ใหม่' ใน JavaScript สิ่งแรกที่คุณควรสังเกตคือหลังจากใช้ 'ใหม่' ในตัวอย่างของเราเราสามารถเรียกเมธอด (sayName) บน 'tyler' เหมือนกับว่ามันเป็นวัตถุ - นั่นเป็นเพราะมันเป็น ดังนั้นก่อนอื่นเรารู้ว่าตัวสร้างบุคคลของเราส่งคืนวัตถุไม่ว่าเราจะเห็นว่าในโค้ดหรือไม่ ประการที่สองเรารู้ว่าเนื่องจากฟังก์ชั่น sayName ของเราตั้งอยู่บนต้นแบบและไม่ได้อยู่บนอินสแตนซ์ Person โดยตรงวัตถุที่ฟังก์ชัน Person กำลังส่งคืนจะต้องมอบสิทธิ์ให้กับต้นแบบในการค้นหาที่ล้มเหลว ในแง่ง่ายขึ้นเมื่อเราเรียก tyler.sayName () ล่ามบอกว่า“ ตกลงฉันจะดูที่วัตถุ 'tyler' ที่เราเพิ่งสร้างขึ้นค้นหาฟังก์ชัน sayName แล้วเรียกมัน เดี๋ยวก่อนฉันไม่เห็นที่นี่ - ทั้งหมดที่ฉันเห็นคือชื่อและอายุ ให้ฉันตรวจสอบต้นแบบ ใช่ดูเหมือนว่ามันอยู่บนต้นแบบให้ฉันเรียกมันว่า”

ด้านล่างคือรหัสสำหรับวิธีที่คุณสามารถคิดเกี่ยวกับสิ่งที่คำหลัก 'ใหม่' ทำงานจริงใน JavaScript มันเป็นตัวอย่างรหัสของย่อหน้าข้างต้น ฉันใส่ 'มุมมองล่าม' หรือวิธีที่ล่ามเห็นโค้ดในบันทึกย่อ

var Person = function(name, age){
  //The line below this creates an obj object that will delegate to the person's prototype on failed lookups.
  //var obj = Object.create(Person.prototype);

  //The line directly below this sets 'this' to the newly created object
  //this = obj;

  this.name = name;
  this.age = age;

  //return this;
}

ตอนนี้มีความรู้ในสิ่งที่คำหลัก 'ใหม่' นี้จริงๆใน JavaScript การสร้างบริการในเชิงมุมควรจะเข้าใจง่ายขึ้น

สิ่งที่ใหญ่ที่สุดที่ควรทำความเข้าใจเมื่อสร้างบริการคือการรู้ว่าบริการได้รับการยกตัวอย่างด้วยคำหลัก 'ใหม่' เมื่อรวมความรู้ดังกล่าวกับตัวอย่างของเราข้างต้นแล้วคุณควรตระหนักว่าคุณกำลังแนบคุณสมบัติและวิธีการของคุณโดยตรงกับ 'นี้' ซึ่งจะส่งคืนจากบริการ ลองดูที่การทำงานนี้

ซึ่งแตกต่างจากที่เราทำกับตัวอย่างโรงงานในตอนแรกเราไม่จำเป็นต้องสร้างวัตถุจากนั้นคืนค่าวัตถุนั้นเพราะอย่างที่เคยกล่าวไว้หลายครั้งก่อนหน้านี้เราใช้คำหลัก 'ใหม่' เพื่อที่ล่ามจะสร้างวัตถุนั้น มันเป็นต้นแบบแล้วส่งคืนให้เราโดยที่เราไม่ต้องทำงาน

ก่อนอื่นมาสร้างฟังก์ชั่น 'ส่วนตัว' และตัวช่วยของเรา สิ่งนี้ควรดูคุ้นเคยเพราะเราทำสิ่งเดียวกันกับโรงงานของเรา ฉันจะไม่อธิบายว่าแต่ละบรรทัดทำอะไรที่นี่เพราะฉันทำอย่างนั้นในตัวอย่างจากโรงงานถ้าคุณงงให้อ่านตัวอย่างจากโรงงานอีกครั้ง

app.service('myService', function($http, $q){
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }
});

ตอนนี้เราจะแนบวิธีการทั้งหมดของเราที่จะมีอยู่ในตัวควบคุมของเรากับ 'นี้'

app.service('myService', function($http, $q){
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }

  this.setArtist = function(artist){
    _artist = artist;
  }

  this.getArtist = function(){
    return _artist;
  }

  this.callItunes = function(){
    makeUrl();
    var deferred = $q.defer();
    $http({
      method: 'JSONP',
      url: _finalUrl
    }).success(function(data){
      deferred.resolve(data);
    }).error(function(){
      deferred.reject('There was an error')
    })
    return deferred.promise;
  }

});

ตอนนี้เหมือนในโรงงานของเรา setArtist, getArtist และ callItunes จะมีอยู่ในตัวควบคุมใดก็ตามที่เราส่งผ่าน myService ไป นี่คือตัวควบคุม myService (ซึ่งเกือบจะเหมือนกับตัวควบคุมโรงงานของเรา)

app.controller('myServiceCtrl', function($scope, myService){
  $scope.data = {};
  $scope.updateArtist = function(){
    myService.setArtist($scope.data.artist);
  };

  $scope.submitArtist = function(){
    myService.callItunes()
      .then(function(data){
        $scope.data.artistData = data;
      }, function(data){
        alert(data);
      })
  }
});

อย่างที่ฉันเคยพูดเมื่อคุณเข้าใจสิ่งที่ 'ใหม่' ทำบริการเกือบจะเหมือนโรงงานใน Angular


12
คุณอาจต้องการระบุลิงก์ไปยังบล็อกของคุณโดยตรง tylermcginnis.com/angularjs-factory-vs-service-vs-providerฉันพบว่าอ่านง่ายขึ้นเล็กน้อย
Tyler Collier

3
Nothin ผิดกับการทำซ้ำบล็อกของคุณที่นี่ แต่ฉันยอมรับว่าเป็นโพสต์บล็อกของเกรตา
R Claven

5
คำอธิบายรายละเอียดที่ดีเกี่ยวกับสิ่งที่แต่ละคนทำภายใต้ประทุน แต่ก็ยังไม่ชัดเจนว่าทำไมและเมื่อไรที่ใครบางคนเลือกที่จะใช้บริการผ่านโรงงาน กล่าวอีกนัยหนึ่งฉันจะชอบวัตถุใหม่เมื่อเทียบกับวัตถุที่ถูกส่งคืนจากโรงงาน ฉันคิดว่านี่เป็นความสับสนที่ยิ่งใหญ่ที่สุด
demisx

2
โดยทั่วไปหากคุณต้องการสร้างการเชื่อมต่อแบบถาวรกับบริการระยะไกลเช่น iTunes API ที่กล่าวถึงในตัวอย่างด้วยการเชื่อมต่อคงที่ (สถานะการเชื่อมต่อประวัติการโทรการจัดเก็บข้อมูล) คุณสามารถไปกับ Factory หากคุณใช้งานเป็นบริการทุกครั้งที่คุณต้องการบางสิ่งจาก API คุณจะต้องสร้างการเชื่อมต่อใหม่และไม่สามารถเก็บอะไรไว้ในนั้นได้ เนื่องจากทุกครั้งที่คุณสร้างบริการขึ้นใหม่คุณจะได้รับวัตถุเปล่า / ค่าเริ่มต้น
Meki

4
ฉันไม่คิดว่าถูกต้อง @Aznim เหมือนที่คนอื่นพูดกัน
Cryptovirus

35

เบาะแสอยู่ในชื่อ

บริการและโรงงานมีความคล้ายคลึงกัน ทั้งสองจะให้ผลลัพธ์วัตถุเดี่ยวที่สามารถฉีดเข้าไปในวัตถุอื่น ๆ และมักจะใช้แทนกันได้

พวกเขาตั้งใจที่จะใช้ความหมายในการใช้รูปแบบการออกแบบที่แตกต่างกัน

บริการใช้สำหรับการปรับใช้รูปแบบการบริการ

รูปแบบการบริการเป็นสิ่งหนึ่งที่แอปพลิเคชันของคุณแบ่งออกเป็นฟังก์ชันการทำงานที่สอดคล้องตามหลักเหตุผล ตัวอย่างอาจเป็นตัวเข้าถึง API หรือชุดของตรรกะทางธุรกิจ

สิ่งนี้มีความสำคัญอย่างยิ่งในแองกูลาร์เนื่องจากโมเดลของแองกูลาร์มักจะเป็นเพียงวัตถุ JSON ที่ดึงมาจากเซิร์ฟเวอร์ดังนั้นเราจึงต้องการที่อื่นเพื่อวางตรรกะทางธุรกิจของเรา

นี่คือบริการ Github เช่น มันรู้วิธีคุยกับ Github มันรู้เกี่ยวกับ URL และวิธีการ เราสามารถอัดมันเข้าไปในคอนโทรลเลอร์และมันจะสร้างและคืนสัญญา

(function() {
  var base = "https://api.github.com";

  angular.module('github', [])
    .service('githubService', function( $http ) {
      this.getEvents: function() {
        var url = [
          base,
          '/events',
          '?callback=JSON_CALLBACK'
        ].join('');
        return $http.jsonp(url);
      }
    });
  )();

โรงงานใช้รูปแบบของโรงงาน

ในทางกลับกันโรงงานมีจุดประสงค์เพื่อใช้รูปแบบของโรงงาน รูปแบบของโรงงานที่เราใช้ฟังก์ชั่นจากโรงงานเพื่อสร้างวัตถุ โดยทั่วไปแล้วเราอาจใช้สิ่งนี้สำหรับสิ่งปลูกสร้าง นี่คือโรงงานที่ส่งคืน Constructor ของผู้แต่ง:

angular.module('user', [])
  .factory('User', function($resource) {
    var url = 'http://simple-api.herokuapp.com/api/v1/authors/:id'
    return $resource(url);
  })

เราจะใช้ประโยชน์จากสิ่งนี้เช่นนั้น:

angular.module('app', ['user'])
  .controller('authorController', function($scope, User) {
    $scope.user = new User();
  })

โปรดทราบว่าโรงงานจะส่งคืนซิงเกิลตันด้วย

โรงงานสามารถส่งคืนคอนสตรัคเตอร์ได้

เนื่องจากโรงงานส่งคืนวัตถุก็สามารถส่งคืนวัตถุประเภทใดก็ได้ที่คุณต้องการรวมถึงฟังก์ชันตัวสร้างตามที่เราเห็นด้านบน

โรงงานส่งคืนวัตถุ บริการเป็น newable

ความแตกต่างทางเทคนิคอีกประการหนึ่งก็คือวิธีการให้บริการและโรงงานประกอบ ฟังก์ชั่นบริการจะถูกสร้างขึ้นใหม่เพื่อสร้างวัตถุ ฟังก์ชั่นจากโรงงานจะถูกเรียกและจะคืนค่าวัตถุ

  • บริการเป็นสิ่งก่อสร้างที่สร้างได้ใหม่
  • โรงงานจะถูกเรียกและส่งคืนวัตถุ

ซึ่งหมายความว่าในบริการเราผนวกเข้ากับ "สิ่งนี้" ซึ่งในบริบทของตัวสร้างจะชี้ไปที่วัตถุที่อยู่ระหว่างการก่อสร้าง

เพื่อแสดงให้เห็นสิ่งนี้นี่เป็นวัตถุง่าย ๆ ที่สร้างขึ้นโดยใช้บริการและโรงงาน:

angular.module('app', [])
  .service('helloService', function() {
    this.sayHello = function() {
      return "Hello!";
    }
  })
  .factory('helloFactory', function() {
    return {
      sayHello: function() {
        return "Hello!";
      }
    }
  });

2
คำอธิบายที่ดีขอบคุณ! นอกจากนี้ยังมีประเภทในโรงงานตัวอย่างรหัสที่พารามิเตอร์หัวฉีดควรจะเป็นAuthor Person
mikhail-t

ขอบคุณ @ mik-T ฉันแก้ไขความผิดพลาด
superluminary

1
รูปแบบการใช้บริการของคุณไม่ถูกต้อง - ควรเป็นโรงงาน ถ้าคุณเรียกว่า. โรงงาน () แทนที่จะเป็น. บริการ () คุณจะเห็นว่ามันใช้งานได้เหมือนกันทุกประการ รูปแบบการบริการหมายถึงการให้มาพร้อมกับฟังก์ชันตัวสร้างไม่ใช่ฟังก์ชันที่ส่งคืนวัตถุใหม่ Angular (อย่างมีประสิทธิภาพ) เรียกว่า "ใหม่" ในฟังก์ชันตัวสร้างของคุณ เหตุผลเดียวที่บริการของคุณทำงานคือถ้าคุณเรียกว่า "ใหม่" บนฟังก์ชันตัวสร้างที่ส่งคืนวัตถุคุณจะได้รับวัตถุที่ถูกส่งคืนกลับมาแทนที่จะสร้างขึ้นมาใหม่ และโรงงานสามารถใช้สร้างสิ่งที่คุณต้องการไม่ใช่แค่แบบจำลอง
Dan King

27

คำตอบทั้งหมดที่นี่ดูเหมือนจะเป็นรอบการบริการและโรงงานและที่ถูกต้องตั้งแต่นั้นเป็นสิ่งที่ถูกถามเกี่ยวกับ แต่มันก็ยังเป็นสิ่งสำคัญที่จะเก็บไว้ในใจว่ามีคนอื่น ๆ หลายคนรวมทั้งprovider(), และvalue()constant()

กุญแจสำคัญในการจำคือแต่ละคนเป็นกรณีพิเศษของอีกคนหนึ่ง แต่ละกรณีพิเศษลงโซ่ช่วยให้คุณทำสิ่งเดียวกันกับรหัสน้อย แต่ละคนมีข้อ จำกัด เพิ่มเติมบางอย่างเช่นกัน

ในการตัดสินใจว่าจะใช้เมื่อใดซึ่งคุณเพิ่งเห็นว่าอันไหนให้คุณทำสิ่งที่คุณต้องการในโค้ดที่น้อยกว่า นี่คือภาพที่แสดงให้เห็นว่ามีความคล้ายคลึงกันอย่างไร:

ป้อนคำอธิบายรูปภาพที่นี่

สำหรับการแยกทีละขั้นตอนอย่างสมบูรณ์และการอ้างอิงอย่างรวดเร็วว่าจะใช้เมื่อใดคุณสามารถไปที่โพสต์บล็อกที่ฉันได้รับภาพนี้จาก:

http://www.simplygoodcode.com/2015/11/the-difference-between-service-provider-and-factory-in-angularjs/


3
@jacob อาจเป็นเช่นนั้น แต่ฉันคิดว่าแนวคิดโดยรวมของไม่เพียง แต่เมื่อจะใช้แต่ละครั้ง
Luis Perez

1
@LuisPerez ลิงก์ไปยังบล็อกของคุณและวิดีโออธิบายความแตกต่างที่ยอดเยี่ยมจริงๆ ง่ายต่อการเข้าใจกับตัวอย่างเหล่านั้นจากวิดีโอ :)
Alin Ciocan

24

app.factory ('fn', fn) กับ app.service ('fn', fn)

การก่อสร้าง

สำหรับโรงงาน Angular จะเรียกใช้ฟังก์ชันเพื่อรับผลลัพธ์ เป็นผลลัพธ์ที่ถูกแคชและฉีด

 //factory
 var obj = fn();
 return obj;

ด้วยบริการเชิงมุมจะเรียกฟังก์ชั่นคอนสตรัคโดยการเรียกใหม่ ฟังก์ชั่นที่สร้างขึ้นจะถูกแคชและฉีด

  //service
  var obj = new fn();
  return obj;

การดำเนินงาน

โดยทั่วไปโรงงานจะส่งคืนวัตถุตามตัวอักษรเนื่องจากค่าส่งคืนคือสิ่งที่ถูกฉีดเข้าไปในตัวควบคุมเรียกใช้บล็อกคำสั่ง ฯลฯ

  app.factory('fn', function(){
         var foo = 0;
         var bar = 0;
         function setFoo(val) {
               foo = val;
         }
         function setBar (val){
               bar = val;
         }
         return {
                setFoo: setFoo,
                serBar: setBar
         }
  });

ฟังก์ชั่นบริการมักจะไม่ส่งคืนสิ่งใด แต่จะดำเนินการเตรียมใช้งานและแสดงฟังก์ชันแทน ฟังก์ชั่นยังสามารถอ้างอิง 'นี่' ได้เนื่องจากมันถูกสร้างโดยใช้ 'ใหม่'

app.service('fn', function () {
         var foo = 0;
         var bar = 0;
         this.setFoo = function (val) {
               foo = val;
         }
         this.setBar = function (val){
               bar = val;
         }
});

ข้อสรุป

เมื่อมาถึงการใช้โรงงานหรือบริการพวกเขาทั้งสองคล้ายกันมาก พวกเขาจะถูกฉีดเข้าไปในตัวควบคุมคำสั่งเรียกใช้บล็อก ฯลฯ และใช้ในรหัสลูกค้าในลักษณะเดียวกัน ทั้งสองแบบยังเป็นแบบซิงเกิล - หมายถึงอินสแตนซ์เดียวกันจะถูกใช้ร่วมกันระหว่างทุกสถานที่ที่ให้บริการ / โรงงาน

คุณควรเลือกแบบไหนดี? อย่างใดอย่างหนึ่ง - พวกเขาคล้ายกันมากกับความแตกต่างเล็กน้อย หากคุณเลือกอย่างใดอย่างหนึ่งเพียงแค่ตระหนักถึงวิธีการสร้างพวกเขาเพื่อให้คุณสามารถใช้พวกเขาอย่างถูกต้อง


ฟังก์ชั่นบริการไม่ "ไม่คืนสิ่งใด" โดยปริยายจะส่งคืนออบเจ็กต์ที่สร้างขึ้นหากคุณไม่ระบุคำสั่งส่งคืนของคุณเอง (ในกรณีหลังวัตถุที่คุณส่งคืนคือสิ่งที่จะถูกสร้างและแคชคล้ายกับโรงงาน)
Cryptovirus

ฉันคิดว่าคุณตีความผิด ... เมื่อฉันพูดกลับฉันหมายถึงจากมุมมองจากการใช้งานฟังก์ชั่นบริการ
pixelbits

คุณแน่ใจหรือว่าโรงงานนี้เป็นเมืองเดียว
Martian2049

5

ฉันใช้เวลาพยายามหาความแตกต่าง

และฉันคิดว่าฟังก์ชั่นจากโรงงานใช้รูปแบบโมดูลและฟังก์ชั่นบริการใช้รูปแบบตัวสร้างสคริปต์จาวามาตรฐาน


2

รูปแบบของโรงงานมีความยืดหยุ่นมากขึ้นเนื่องจากสามารถส่งคืนฟังก์ชั่นและค่าเช่นเดียวกับวัตถุ

รูปแบบการบริการมีไม่มาก IMHO เหมือนทุกสิ่งที่คุณสามารถทำได้อย่างง่ายดายเช่นเดียวกับโรงงาน ข้อยกเว้นอาจเป็น:

  • หากคุณสนใจประเภทที่ประกาศของบริการอินสแตนซ์ของคุณด้วยเหตุผลบางประการ - ถ้าคุณใช้รูปแบบการบริการคอนสตรัคเตอร์ของคุณจะเป็นประเภทของบริการใหม่
  • หากคุณมีฟังก์ชั่นคอนสตรัคเตอร์ที่คุณใช้อยู่ในที่อื่นซึ่งคุณต้องการใช้เป็นบริการด้วย (แม้ว่าอาจจะไม่ค่อยได้ใช้งานเท่าไหร่

รูปแบบการบริการคือ วิธีที่ดีกว่าเล็กน้อยในการสร้างออบเจ็กต์ใหม่จากมุมมองไวยากรณ์ แต่ก็มีค่าใช้จ่ายสูงกว่าในการสร้างอินสแตนซ์ คนอื่น ๆ ระบุว่าเชิงมุมใช้ "ใหม่" เพื่อสร้างบริการ แต่สิ่งนี้ไม่เป็นความจริง - มันไม่สามารถทำได้เพราะผู้ให้บริการทุกคนมีพารามิเตอร์ที่แตกต่างกัน สิ่งที่เชิงมุมทำคือใช้รูปแบบจากโรงงานเพื่อห่อฟังก์ชันตัวสร้างของคุณ จากนั้นมันจะทำ jiggery pokery ที่ชาญฉลาดเพื่อจำลองโอเปอเรเตอร์ "ใหม่" ของ javascript ซึ่งเรียกใช้คอนสตรัคเตอร์ของคุณด้วยจำนวนตัวแปรที่สามารถฉีดได้ - แต่คุณสามารถออกจากขั้นตอนนี้ได้ถ้าคุณใช้รูปแบบจากโรงงานโดยตรง รหัส.


บริการมีประสิทธิภาพในการสร้างมากกว่าโรงงานเนื่องจากโรงงานใช้การปิดที่ค่อนข้างแพงและบริการ (คลาส) สามารถใช้ประโยชน์จากต้นแบบได้
jacob

@jacob ไม่แน่ใจว่าคุณหมายถึงอะไรเกี่ยวกับการปิดหรือไม่ โรงงานเป็นเพียงฟังก์ชันที่คืนค่าวัตถุ คุณจะต้องใช้การปิดถ้าวัตถุที่ส่งคืนของคุณต้องการสถานะ "ส่วนตัว" คุณยังคงต้องทำสิ่งเดียวกันถ้าคุณใช้ตัวสร้าง (บริการ) ฉันใช้จุดของคุณเกี่ยวกับต้นแบบแม้ว่า - แม้ว่าคุณยังสามารถทำได้ในโรงงานถ้าคุณต้องการ
Dan King

function MyFactory(dep1) { var $$foo = 'bar', factory = {}; Object.defineProperties(factory.prototype, { foo: { value: $$foo } }); return factory; } function MyService(dep1) { var $$foo = 'bar'; Object.defineProperties(MyService.prototype, { foo: { value: $$foo } }); } ในขณะที่ทั้ง MyFactory และ MyService ใช้ต้นแบบ MyFactory ยังคงต้องใช้ประสิทธิภาพในการสร้างวัตถุที่ถูกส่งคืน ในทั้งสองตัวอย่างพวกเขามีเอกชน แต่ใน MyService ไม่มีความแตกต่างด้านประสิทธิภาพ
jacob

1
สำหรับฉันความแตกต่างคือฉันต้องการใช้โรงงานโดยตรงโดยไม่ใช้วิธีการ: MyFactory(someArgument)(อดีต$http()) MyService(someArgument)ว่าเป็นไปไม่ได้ที่มีบริการตามที่คุณต้องการจะสร้างอ้างอิง:
jacob

ในเวลาที่สร้างวัตถุฉันไม่เห็นจริง ๆ ว่า factory = {} มีประสิทธิภาพการทำงานอย่างไรมากกว่าจาวาสคริปต์ที่ให้ค่าเริ่มต้น "this" แก่คุณเมื่อเรียกใช้ Constructor ของคุณ และฉันคิดว่าการตีที่ใหญ่กว่านั้นจะอยู่ที่มุมเชิงมุมเมื่อมันหุ้มตัวสร้างของคุณในโรงงานแล้วต้องกระโดดผ่านห่วงเพื่อจำลอง "ใหม่" เพื่อที่จะฉีดการพึ่งพาของคุณ
ด่าน King
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.