๐ Chapter 5: ์ฝ๋ฐฑ ํจํด
๐ ๋จ์ ํ ์คํธโ
๐ ์ฝ๋ฐฑ ํจ์๋ฅผ ์ฌ์ฉํ ์ฝ๋์ ์์ฑ๊ณผ ํ ์คํ โ
์๊ตฌ์ฌํญ: ์นํ์ ๊ณง ๊ฐ์ต๋ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝํผ๋ฐ์ค ์น ์ฌ์ดํธ์ ๋ด๋น ๊ฐ๋ฐ์๋ค. ๊ทธ๋ ์ฝํผ๋ฐ์ค ์์๋ด์ฌ์๊ฐ ์ฌ์ฉํ ์ฐธ๊ฐ์ ์ฒดํฌ์ธ ํ๋ฉด์ ๊ตฌํํด์ผ ํ๋ค. ์ด ์ ๊ท ํ๋ฉด์ ์ฐธ๊ฐ์ ๋ชฉ๋ก์ ๋ณด์ฌ์ฃผ๊ณ ๊ทธ์ค ํ ์ฌ๋ ๋๋ ์ฌ๋ฌ ์ฌ๋์ ์ ํ(์ฒดํฌ์นญํ ๊ฒ์ผ๋ก ํ์) ํ ๋ค ์ธ๋ถ ์์คํ ๊ณผ ์ฐ๋ํ์ฌ ์ฒดํฌ์ธ์ ์๋ฃํ๋ค. UI ๋ฐฐํ์ ์ฒดํฌ์ธ ๊ธฐ๋ฅ์
checkInService
ํจ์๋ก ๊ตฌํํ๊ธฐ๋ก ํ๋ค.
์ฐธ๊ฐ์๋ฅผ ๊ฐ๊ฐ ์ฒดํฌ์ธํ๋ ค๋ฉด attendeeCollection
๊ฐ์ฒด๋ ์ฐธ๊ฐ์ ๊ฐ์ธ๋ณ๋ก ์ด๋ค ์ก์
์ ์ํํ ์ ์๋ ๊ตฌ์กฐ์ฌ์ผ ํ๋ค. ์นํ์ ๋ฐ๋ก ์ด ์ก์
์ ์ฝ๋ฐฑ ํจ์์ ๋ฃ์ด ์คํํ๊ณ ์ถ๋ค. ๋จผ์ contains
, add
, remove
ํจ์๊ฐ ํ์์ธ attendeeCollection
์ ๋ค์๊ณผ ๊ฐ์ด ์ ์ํ๋ค.
var Conference = Conference || {};
Conference.attendeeCollection = function() {
var attendees = [];
return {
contains: function(attendee) {
return attendees.indexOf(attendee) > -1;
},
add: function(attendee) {
if (!this.contains(attendee)) {
attendees.push(attendee);
}
},
remove: function(attendee) {
var index = attendees.indexOf(attendee);
if (index > -1) {
attendees.splice(index, 1);
}
},
iterate: function(callback) {
// attendees์ ๊ฐ attendee์ ๋ํด ์ฝ๋ฐฑ์ ์คํํ๋ค.
}
}
}
iterate
๋ฅผ ๊ตฌํํ๊ธฐ ์ ์ ์ผ๋จ ๋จ์ ํ
์คํธ๋ฅผ ๋ง๋ค์ด ๊ธฐ๋ฅ์ ๋ค์๊ณผ ๊ฐ์ด ์ ๊ฒํ ์ ์๋ค.
describe('Conference.attendeeCollection', () => {
describe('contains(attendee)', () => {
// contains ํ
์คํธ
});
describe('add(attendee)', () => {
// add ํ
์คํธ
});
describe('remove(attendee)', () => {
// remove ํ
์คํธ
});
describe('iterate(callback)', () => {
var collection, callbackSpy;
// ๋์ฐ๋ฏธ ํจ์
function addAttendeesToCollection(attendeeArray) {
attendeeArray.forEach(function(attendee) {
collection.add(attendee);
});
}
function verifyCallbackWasExecutedForEachAttendee(attendeeArray) {
// ๊ฐ ์์๋ง๋ค ํ ๋ฒ์ฉ ์คํ์ด๊ฐ ํธ์ถ๋์๋์ง ํ์ธํ๋ค.
expect(callbackSpy.calls.count()).toBe(attendeeArray.length);
// ๊ฐ ํธ์ถ๋ง๋ค spy์ ์ ๋ฌํ ์ฒซ ๋ฒ์งธ ์ธ์๊ฐ ํด๋น attendee์ธ์ง ํ์ธํ๋ค.
var allCalls = callbackSpy.calls.all();
for (var i = 0; i < allCalls.length; i++) {
expect(allCalls[i].args[0]).toBe(attendeeArray[i]);
}
}
beforeEach(function() {
collection = Conference.attendeeCollection();
callbackSpy = jasmine.createSpy();
});
it('๋น ์ปฌ๋ ์
์์๋ ์ฝ๋ฐฑ์ ์คํํ์ง ์๋๋ค', () => {
collection.iterate(callbackSpy);
expect(callbackSpy).not.toHaveBeenCalled();
});
it('์์๊ฐ ํ๋๋ฟ์ธ ์ปฌ๋ ์
์ ์ฝ๋ฐฑ์ ํ ๋ฒ๋ง ์คํํ๋ค', () => {
var attendees = [Conference.attendee('์ค์ง', '๊น')];
addAttendeesToCollection(attendees);;
collection.iterate(callbackSpy);
verifyCallbackWasExecutedForEachAttendee(attendees);
});
it('์ปฌ๋ ์
์์๋ง๋ค ํ ๋ฒ์ฉ ์ฝ๋ฐฑ์ ์คํํ๋ค', () => {
var attendees = [
Conference.attendee('์ค์ง', '๊น'),
Conference.attendee('Tom', 'Kazansky'),
Conference.attendee('ํ์', '๊น'),
];
addAttendeesToCollection(attendees);;
collection.iterate(callbackSpy);
verifyCallbackWasExecutedForEachAttendee(attendees);
});
});
});
์ฝ๋ฐฑ ๊ธฐ๋ฅ์ ๋ ์ด ํ ์คํธ๋ ๋ค์ ๋ ๊ฐ์ง๋ฅผ ํ์ธํ๋ค.
- ์ฝ๋ฐฑ ์คํ ํ์๊ฐ ์ ํํ๋ค.
- ์ฝ๋ฐฑ์ด ์คํ๋ ๋๋ง๋ค ์๋ง์ ์ธ์๊ฐ ์ ๋ฌ๋๋ค.
์ด์ attendeeCollection.iterate
ํจ์๋ฅผ ๊ตฌํํ ์ ์๋ค.
var Conference = Conference || {};
Conference.attendeeCollection = function() {
var attendees = [];
return {
// ...
getCount: function() {
return attendees.length;
},
iterate: function(callback) {
attendees.forEach(callback);
}
}
}
attendeeCollection
๊ธฐ๋ฅ ๊ตฌํ์ ๋ง์ณค์ง๋ง, ์ด ์ ์๋ถ๋ถ์์ ์ ์ํ ์๊ฑด์ด ์์ง ๋ค ๋ฐ์๋ ๊ฒ์ ์๋๋ค. ์ฐธ๊ฐ์ ์ฒดํฌ์ธ ํ ์ธ๋ถ ์์คํ
์ ์ฒดํฌ์ธ์ ๋ฑ๋กํ๋ ์ฝ๋๊ฐ ์์ง ๋จ์๋ค.
๐ ์ฝ๋ฐฑ ํจ์์ ์์ฑ๊ณผ ํ ์คํ โ
attendeeCollection
ํ์ ์ก์์ผ๋ ์ถ๊ฐ ๊ธฐ๋ฅ ๊ตฌํ์ ๊ฐ๋ณ ์ฐธ๊ฐ์๋ฅผ ์ฒดํฌ์ธํ๋ ์ฝ๋ฐฑ ํจ์๋งํผ์ด๋ ๊ฐ๋จํ๋ค. ์ฐธ๊ฐ์๋ฅผ ์ฒดํฌ์ธํ๋ ์ต๋ช
ํจ์๋ฅผ attendeeCollection.iterate
ํจ์์ ๋ฐ๋ก ๋ฃ๊ธฐ๋ง ํ๋ฉด ๋๋ค.
var attendees = Conference.attendeeCollection();
// UI์์ ์ ํ๋ ์ฐธ๊ฐ์๋ค์ ์ถ๊ฐํ๋ค.
attendees.iterate(function(attendee) {
attendee.checkIn();
// ์ธ๋ถ ์๋น์ค์ ์ฒดํฌ์ธ์ ๋ฑ๋กํ๋ค.
})
ํจ์๋ฅผ ์ ์ํ๊ธฐ ๋ฌด์ญ๊ฒ ๋ค๋ฅธ ํจ์์ ์ฝ๋ฐฑ์ผ๋ก ๋ฐ๋ก ๋๊ธฐ๋ ๊ฑด ์๋ฐ์คํฌ๋ฆฝํธ์ ๊ฐ๋ ฅํ ํน์ฑ์ด์ง๋ง, ์์นซํ๋ฉด ์ ๋์์ ๋ฒ์ด๋ ์ฐ๋ ค๋ ์๋ค.
์ฒซ์งธ, ์ต๋ช
์ฝ๋ฐฑ ํจ์๋ ์ฝ๋ฐฑ๋ง ๋ฐ๋ก ๋ผ์ด๋ผ ๋ฑ
๋ฒ์ด ์์ด์ ๋จ์ ํ
์คํธ๊ฐ ๋ถ๊ฐ๋ฅํ๋ค. ์ด ์์ ์์๋ ์ฐธ๊ฐ์ ์ฒดํฌ์ธ ๊ธฐ๋ฅ์ด attendeeCollection
์ ๋ฌถ์ฌ ์์ผ๋ฏ๋ก(์ฝ๋ฐฑ ์คํ ์ฌ๋ถ๊ฐ ์๋๋ผ, ์ฐธ๊ฐ์๋ค์ด ์ ๋๋ก ์ฒดํฌ์ธํด์ ๋ฑ๋ก ์ฒ๋ฆฌ๊ฐ ๋๋ฌ๋์ง ํ
์คํธํ ์๋๊ฐ ์๋๋ผ๋ฉด) ์ปฌ๋ ์
์ ํฌํจ๋ ์ฐธ๊ฐ์๋ค์ ์ฒดํฌ์ธ ์ฌ๋ถ๋ ์ ์ฒด ์ปฌ๋ ์
์ ์๋๋ก ๊ณ์ ํ
์คํธ๋ฅผ ๋ฐ๋ณตํ ์๋ฐ์ ์๋ค.
๋์งธ, ์ต๋ช
ํจ์๋ ๋๋ฒ๊น
์ ๋งค์ฐ ์ด๋ ต๊ฒ ๋ง๋ ๋ค. ์ต๋ช
ํจ์๋ ์ ์ ์์ฒด๊ฐ ์ด๋ฆ ์๋ ํจ์๋ผ์ ๋๋ฒ๊ฑฐ๊ฐ ํธ์ถ ์คํ์ ์๋ณ์๋ฅผ ํ์ํ ์ ์๋ค. ์ฐธ์กฐํ ํจ์๋ช
์ด ์๋ ํจ์๋ ์คํ ์ฝํ
์คํธ๋ฅผ ๋ถ๊ฐํ๊ธฐ ์ด๋ ต๊ณ ๊ฒฐ๊ณผ์ ์ผ๋ก ๋๋ฒ๊น
์์ฒด๊ฐ ๋
น๋กํ์ง ์๋ค.
ํ์ง๋ง ์ฝ๋ฐฑ ํจ์์๋ ์ด๋ฆ์ ๋ถ์ผ ์ ์๋๋ฐ, ๊ทธ๋ ๋ค๊ณ ํด์ ํ
์คํธ์ฑ์ด ๋ ์ข์์ง๋ ๊ฑด ์๋์ง๋ง, ์ ์ด๋ ๋๋ฒ๊น
์์
์ ํ๊ฒฐ ์์ํด์ง๋ค.
var attendees = Conference.attendeeCollection();
// UI์์ ์ ํ๋ ์ฐธ๊ฐ์๋ค์ ์ถ๊ฐํ๋ค.
attendees.iterate(function doCheckIn(attendee) {
attendee.checkIn();
// ์ธ๋ถ ์๋น์ค๋ฅผ ํตํด ์ฒดํฌ์ธ ๋ฑ๋กํ๋ค.
});
์ด์ ํธ์ถ ์คํ ๋ชฉ๋ก์ ํจ์๋ช ์ด ํ์๋์ด ์ฝํ ์คํธ๋ฅผ ํ์ ํ ์ ์๊ณ ๋๋ฒ๊น ์์ ์ด ํธํด์ก๋ค.
๋ค๋ฅธ ๋ฐฉ๋ฒ์ผ๋ก ์ฐธ๊ฐ์ ์ฒดํฌ์ธ์ ์ค์ํ ๊ธฐ๋ฅ ์๊ฑด์ด๋ฏ๋ก ๊ฐ๋ ฅ checkInService
๊ฐ์ ์์ฒด ๋ชจ๋์ ์บก์ํํ๋ฉด ํ
์คํธ ๊ฐ๋ฅใ
ํ ๋จ์๋ก ๋ง๋ค ์ ์๊ณ ์ฒดํฌ์ธ ๋ก์ง์ attendeeCollection
์์ ๋ถ๋ฆฌํ์ฌ ์ฝ๋๋ฅผ ์ฌ์ฌ์ฉํ ์๋ ์๋ค.
TDD ๋ฐฉ์์ผ๋ก ๊ฐ๋ฐํ๋ฉด ์ฆํฅ์ ์ด๊ณ ๋์์๋ ์ฝ๋๊ฐ ๋ง๋ค์ด์ง๊น ๋ด ์ผ๋ คํ๋ ์ฌ๋๋ค์ด ๋ง์ง๋ง, ์คํ๋ ค ํ ์คํธ ์ธ๋ถ๋ฅผ ์ฃผ์ ๊น๊ฒ ๋ฐ๋ผ๋ณผ ์ ์์ด ํ๋ก๊ทธ๋จ ๊ตฌ์กฐ๊ฐ ๊ฐ์ ๋๋ ํจ๊ณผ๊ฐ ์๋ค.
๋ค์ ์๋ checkInService.checkIn
ํจ์์ ๊ธฐ๋ณธ ๊ธฐ๋ฅ์ ์ ์ ํ๋ ํ
์คํธ ๊พธ๋ฌ๋ฏธ๋ค.
describe('Conference.checkInService', () => {
var checkInService, checkInRecorder, attendee;
beforeEach(() => {
checkInRecorder = Conference.checkInRecorder();
spyOn(checkInRecorder, 'recordCheckIn');
// checkInRecorder๋ฅผ ์ฃผ์
ํ๋ฉด์
// ์ด ํจ์์ recordCheckIn ํจ์์ ์คํ์ด๋ฅผ ์ฌ๋๋ค.
checkInService = Conference.checkInService(checkInRecorder);
attendee = Conference.attendee('ํ์ฒ ', '์');
});
describe('checkInService.checkIn(attendee)', () => {
it('์ฐธ๊ฐ์๋ฅผ ์ฒดํฌ์ธ ์ฒ๋ฆฌํ ๊ฒ์ผ๋ก ํ์ํ๋ค', () => {
checkInService.checkIn(attendee);
expect(attendee.isCheckedIn()).toBe(true);
});
it('์ฒดํฌ์ธ์ ๋ฑ๋กํ๋ค', () => {
checkInService.checkIn(attendee);
expect(checkInRecorder.recordCheckIn).toHaveBeenCalledWith(attendee);
});
});
});
checkInService.checkIn
๋จ์ ํ
์คํธ๊ฐ ๋ง๋ค์ด์ก๊ณ , checkInService
๊ตฌํ ์ญ์ ๊ฐ๋จํ๋ค.
var Conference = Conference || {};
Conference.checkInService = function(checkInRecorder) {
// ์ฃผ์
ํ checkInRecorder์ ์ฐธ์กฐ๊ฐ์ ๋ณด๊ดํ๋ค.
var recorder = checkInRecorder;
return {
checkIn: function(attendee) {
attendee.checkIn();
recorder.recordCheckIn(attendee);
}
};
};
๋ง์ง๋ง์ผ๋ก ์ฝ๋ฐฑ ํจํด์ผ๋ก ์ฝํผ๋ฐ์ค ์ฐธ๊ฐ์๋ฅผ ์ฒดํฌ์ธํ๋ ๊ธฐ๋ฅ์ด ํ ์คํธ๋ฅผ ํต๊ณผํ, ๋ ๋ฆฝ์ ์ด๊ณ ๋ฏฟ์์ฑ ์๋ 3๊ฐ์ ๋ชจ๋๋ก ์๋ก ์ ์กฐํ๋์ด ์คํ๋๋ ๋ชจ์ต์ด๋ค.
var checkInService = Conference.checkInService(Conference.checkInRecorder());
var attendees = Conference.attendeeCollection();
// UI์์ ์ ํ๋ ์ฐธ๊ฐ์๋ค์ ์ปฌ๋ ์
์ ์ถ๊ฐํ๋ค.
attendees.iterate(checkInService.checkIn);
๐ ๋ฌธ์ ์๋ฐฉโ
์ต๋ช ํจ์๊ฐ ์ค์ ๋ก ์ฝ๋์ ๊ด์ฌ์ฌ๋ฅผ ๋ถ๋ฆฌํ๊ณ ํ ์คํธํ๊ธฐ ์ด๋ ต๊ฒ ๋ง๋ค์ด ๊ฒฐ๊ตญ ๋ฏฟ์์ฑ์ด ๋จ์ด์ง๋ค๋ ์ด์ผ๊ธฐ๋ ํ๋ค.
๊ทธ๋ฐ๋ฐ ์ฝ๋ฐฑ ํจํด์ ๋ฏฟ์์ฑ์ ๋์ด๋ด๋ฆฌ๋ ์์ธ์ ์ต๋ช
์ฝ๋ฐฑ ํจ์๋ฟ๋ง์ด ์๋๋ค. ์ฝ๋ฐฑ ํ์ด์ด๋ผ๋ ๊ณจ์นซ๋ฉ์ด์ ์ฝ๋ฐฑ ํจ์์์ ์๋ฑ์ ๊ฐ์ ๊ฐ๋ฆฌํค๋ this
, ๋ ๊ฐ์ง ๋ฌธ์ ์ ์ ์ด๋ป๊ฒ ์๋ฐฉํ ์ ์๋์ง ์์๋ณธ๋ค.
๐ ์ฝ๋ฐฑ ํ์ด ๋๋ฌ ํด๊ธฐโ
์ฝ๋ฐฑ ํ์ด(callback arrow)์ ์ฝ๋ฐฑ ํจ์๋ฅผ ๋จ๋ฐํ ๊ทน๋จ์ ์ธ ์ผ์ด์ค๋ค. ๋ค์ ์์ ๋ฅผ ์ดํด๋ณด์.
CallbackArrow = CallbackArrow || {};
CallbackArrow.rootFunction = function() {
CallbackArrow.firstFunction(function(arg) {
// ์ฒซ ๋ฒ์งธ ์ฝ๋ฐฑ ๋ก์ง
CallbackArrow.secondFunction(function(arg) {
// ๋ ๋ฒ์ฉจ ์ฝ๋ฐฑ ๋ก์ง
CallbackArrow.thirdFunction(function(arg) {
// ์ธ ๋ฒ์งธ ์ฝ๋ฐฑ ๋ก์ง
CallbackArrow.fourthFunction(function(arg) {
// ๋ค ๋ฒ์งธ ์ฝ๋ฐฑ ๋ก์ง
});
});
});
});
};
CallbackArrow.firstFunction = function(callback1) {
callback1(arg);
};
CallbackArrow.secondFunction = function(callback2) {
callback2(arg);
};
CallbackArrow.thirdFunction = function(callback3) {
callback3(arg);
};
CallbackArrow.fourthFunction = function(callback4) {
callback4(arg);
};
์๋ก ์ค์ฒฉ๋ ์ฝ๋ฐฑ๋ค์ด ์ ์ ๊น์ ๋ช์ ๋น ์ ธ๋๋ ๋ชจ์ต์ด ์ผ์ชฝ์์ ์ค๋ฅธ์ชฝ์ผ๋ก ํฅํ๋ ๊ณต๋ฐฑ ํ์ดํ ํ์์ ๋ค๋ค. ์ด ์ฝ๋๋ ์ฝ๊ธฐ๋ ๋ฌผ๋ก ๊ณ ์น๊ธฐ๋ ์ด๋ ต๊ณ ๋จ์ ํ ์คํธ๋ ์ฌ์ค ๋ถ๊ฐ๋ฅํ๋ค.
์ด๋ด ๊ฒฝ์ฐ, ์ต๋ช ํจ์์ ์ด๋ฆ์ ๋ถ์ฌ ๋ผ์ด ๋๊ธฐ๋ง ํด๋ ์ํฉ์ ํจ์ฌ ํธ์ ๋๋ค. ๋ค์์ฒ๋ผ ๋ฆฌํฉํ ๋งํ์.
CallbackArrow = CallbackArrow || {};
CallbackArrow.rootFunction = function() {
CallbackArrow.firstFunction(CallbackArrow.firstCallback);
};
CallbackArrow.firstFunction = function(callback1) {
callback1(arg);
};
CallbackArrow.secondFunction = function(callback2) {
callback2(arg);
};
CallbackArrow.thirdFunction = function(callback3) {
callback3(arg);
};
CallbackArrow.fourthFunction = function(callback4) {
callback4(arg);
};
CallbackArrow.firstCallback = function() {
// ์ฒซ ๋ฒ์งธ ์ฝ๋ฐฑ ๋ก์ง
CallbackArrow.secondFunction(CallbackArrow.secondCallback);
};
CallbackArrow.secondCallback = function() {
// ๋ ๋ฒ์งธ ์ฝ๋ฐฑ ๋ก์ง
CallbackArrow.thirdFunction(CallbackArrow.thirdCallback);
};
CallbackArrow.thirdCallback = function() {
// ์ธ ๋ฒ์งธ ์ฝ๋ฐฑ ๋ก์ง
CallbackArrow.fourthFunction(CallbackArrow.fourthCallback);
};
CallbackArrow.fourthFunction = function() {
// ๋ค ๋ฒ์งธ ์ฝ๋ฐฑ ๋ก์ง
};
์ด์ฒ๋ผ ์ค์ฒฉ ์ฝ๋ฐฑ์ ๋๋ฌ ํธ ์ฝ๋๊ฐ ๋ ๋ซ๋ค. ๋ฌด์๋ณด๋ค ๋จ์ ํ
์คํธ๋ฅผ ์ ์ ์ผ๋ก ํ ์ ์๋ค๋ ์ ์ด ์๋์ด๋ค. ์ค์ฒฉ ์ต๋ช
์ฝ๋ฐฑ ํจ์์ ๋ชจ๋ ๊ธฐ๋ฅ์ ๊ทธ ์์ฒด๋ง์ผ๋ก ๋จ์ ํ
์คํธํ ์ ์๋ CallbackArrow
์ ํจ์ ํ๋กํผํฐ๋ก ๋นผ๋ธ ๋๋ถ์ด๋ค. ๋๊ตฌ๋ ์ฝ๋ฐฑ ํจ์๋ง๋ค ๋ช
์ฐฐ์ด ๋ฌ๋ ค ์์ผ๋ ๋๋ฒ๊น
๋๊ตฌ์์ ์คํ ์ถ์ ์ ์ด์์์ ์ง์ ์ผ๋ ์์ ๊ฒ์ด๋ค.
๐ this๋ฅผ ์กฐ์ฌํ๋ผโ
์ ํ ์๋ฑํ ๊ฐ์ด ์ฐธ์กฐํ ์ ์๊ธฐ ๋๋ฌธ์ ์ฝ๋ฐฑ ํจ์์์ this
๋ณ์๋ฅผ ์ฐธ์กฐํ ๋๋ ์กฐ์ฌํด์ผ ํ๋ค.
์นํ์ ์ฒดํฌ์ธ์ ๋ง์น attendeeCollection
์ attendee
๊ฐ์ฒด ์๋ฅผ ์ธ๋ checkedInAttendeeCounter
๋ชจ๋์ ๊ตฌํํ๋ ค๊ณ ํ๋ค. checkInService
์ ํฌ๊ฒ ๋ค๋ฅผ ๋ฐ ์์ด attendeeCollection.iterate
์ ํ์ถํ ํจ์๋ฅผ ๋๊ธฐ๋ ์์ผ๋ก ์์ฑํ๋ฉด ๋๋ค. checkedInAttendeeCounter
์ ๋จ์ ํ
์คํธ ์ฝ๋๋ฅผ ๋จผ์ ์์ฑํ๋ค.
describe('Conference.checkedInAttendeeCounter', () => {
var counter;
beforeEach(() => {
counter = Conference.checkedInAttendeeCounter();
});
describe('increment()', () => {
// increment ํ
์คํธ
});
describe('getCount()', () => {
// getCount ํ
์คํธ
});
describe('countIfCheckedIn(attendee)', () => {
var attendee;
beforeEach(() => {
attendee = Conference.attendee('ํ์', '๊น');
});
it('์ฐธ๊ฐ์๊ฐ ์ฒดํฌ์ธํ์ง ์์์ผ๋ฉด ์ธ์์๋ฅผ ์ธ์ง ์๋๋ค.', () => {
counter.countIfCheckedIn(attendee);
expect(counter.getCount()).toBe(0);
});
it('์ฐธ๊ฐ์๊ฐ ์ฒดํฌ์ธํ๋ฉด ์ธ์์๋ฅผ ์ผ๋ค', () => {
attendee.checkIn();
counter.countIfCheckedIn(attendee);
expect(counter.getCount()).toBe(1);
});
});
});
์ด์ด์ Conference.checkedInAttendeeCounter
๊ตฌํ๋ถ์ด๋ค.
var Conference = Conference || {};
Conference.checkedInAttendeeCounter = function() {
var checkedInAttendees = 0;
return {
increment: function() {
checkedInAttendees++;
},
getCount: function() {
return checkedInAttendees;
},
countIfCheckedIn: function(attendee) {
if (attendee.isCheckedIn()) {
this.increment();
}
}
}
}
์ฌ๊ธฐ์ this.increment
๋ฅผ ๋ณด๋ฉด ๋จ์ ํ
์คํธ๋ ํต๊ณผํ์ง๋ง checkedInAttendeeCounter
๋ฅผ ์ ๋ง attendeeCollection
์ธ์คํด์ค์ ํจ๊ป ์ธ ์ ์์๊น?
var checkInService = Conference.checkInService(Conference.checkInRecorder());
var attendees = Conference.attendeeCollection();
var counter = Conference.checkedInAttendeeCounter();
// UI์์ ์ ํํ ์ฐธ๊ฐ์๋ค ์ ์ฐธ๊ฐ์ ์ปฌ๋ ์
์ ์ถ๊ฐํ๋ค.
attendees.add(Conference.attendee('์ค์ง', '๊น'));
attendees.add(Conference.attendee('์น๋ฏผ', '์ฌ'));
// ์ฐธ์์๋ค์ ์ฒดํฌ์ธํ๋ค.
attendees.iterate(checkInService.checkIn);
// ์ฒดํฌ์ธ์ ๋ง์น ์ฐธ๊ฐ์ ์ธ์์๋ฅผ ์ธ์ด๋ณธ๋ค.
attendees.iterate(counter.countIfCheckedIn);
console.log(counter.getCount()); // 0 ์ด๋??ใด
์ ์์ ์ ๊ฒฐ๊ณผ๋ 2๊ฐ ๋์์ผ ํ๋ค. attendeeCollection.iterator
์คํ ์ checkedInAttendeeCounter
์์ this
๊ฐ ์ค์ ๋ก ๊ฐ๋ฆฌํจ ๊ฐ์ checkedInAttendeeCounter
๊ฐ ์๋๋ผ ์ ์ญ window
๊ฐ์ฒด์์ ์ ์ ์๋ค.
์ผ๋ฐ์ ์ผ๋ก this
๊ฐ์ ํจ์๋ฅผ ํธ์ถํ (๋๊ฐ ํจ์ ์์ ์ ์ผ๋ก ์ฐ๊ฒฐํ) ๊ฐ์ฒด๋ฅผ ๊ฐ๋ฆฌํค์ง๋ง, ์ฝ๋ฐฑ ํจ์๋ฅผ ๋ง๋ค์ด ๋ฃ์ ๋ ์ด๋ค ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ๋ผ๊ณ ์ง์ ์ง์ ํ ์๋ ์๋ค. ์ด๋ฐ ์ด์ ๋ก ์ฝ๋ฐฑ ํจ์๋ ๋๋ถ๋ถ this
๋ฅผ ๋ช
์์ ์ผ๋ก ๊ฐ๋ฆฌํจ๋ค.
attendeeCollection.iterate
์์ forEach
๋ ์ฝ๋ฐฑ ๋ด๋ถ์์ ์ฐธ์กฐํ ๊ฐ์ฒด๋ฅผ ๋ ๋ฒ์งธ ์ธ์๋ก ์ ๋ฌํจ์ผ๋ก์จ this
๊ฐ ๋ฌด์์ ์ฐธ์กฐํด์ผ ํ๋์ง ๋ฐํ ์ ์๋ค. attendeeCollection.iterate
๊ฐ๋ฐ ๋ด๋น์ ์นํ์ attendeeCollection.iterate
๋ก ํ์ฌ๊ธ this
๊ฐ ์ฐธ์กฐํ ๊ฐ์ฒด๋ฅผ ๋ ๋ฒ์งธ ์ธ์๋ก ๋ฐ์ forEach
์ ๊ทธ๋๋ก ๋๊ฒจ์ฃผ๊ฒ๋ ๊ณ ์น๋ค. ์ด๋ ๊ฒ ํด์ countIfCheckedIn
ํจ์์ ๊ฑธ๋ง์ this
๋ฅผ checkedInAttendeeCounter
์ธ์คํด์ค์ ๋ฌถ์ด๋ ์ ์๋ค.
๊ทธ๋ฌ๋ ๋ง์ผ attendeeCollection
์ด ์ ์
์ฒด๊ฐ ๋ฉํํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฝ๋๋ผ ์นํ์ด ์์ ํ ์ ์๋ ๊ฒฝ์ฐ๋ผ๋ฉด ์์ฑํ ์ฝ๋ฐฑ์์ ์์ ์ ์ผ๋ก ํ์ฌ ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ๊ฐ๋ฆฌํค๋๋ก ํ ์ ์๋ค.
์ฐ์ , checkedInAttendeeCounter.countIfCheckedIn
์คํ ์ this
๊ฐ checkedInAttendeeCounter
์ธ์คํด์ค ์ด์ธ์ ๊ฐ์ฒด๋ฅผ ๊ฐ๋ฆฌํค๋ ์ํฉ์ ๊ฐ์ ํ ๋จ์ ํ
์คํธ๋ฅผ ์ง๋ณด์.
๋ฒ๊ทธ๊ฐ ์๋ค๋ ๊ฑด ํ ์คํธ ๊พธ๋ฌ๋ฏธ๊ฐ ์์ง ๋ ๋๋ค๋ ๋ฐ์ฆ์ด๋ค. ํญ์ ๋ฒ๊ทธ๋ฅผ ๊ณ ์น๊ธฐ ์ ์ ์คํจํ ํ ์คํธ๋ฅผ ๋จผ์ ์์ฑํ๋ผ.
describe('Conference.checkedInAttendeeCounter', () => {
var counter;
// ์ด์ ํ
์คํธ ์ค์
describe('countIfCheckedIn(attendee)', () => {
var attendee;
beforeEach(() => {
attendee = Conference.attendee('ํ์', '๊น');
});
//์ด์ ํ
์คํธ ์ค์
it('this๊ฐ ๊ผญ checkedInAttendeeCounter ์ธ์คํด์ค๋ฅผ ๊ฐ๋ฆฌํค๋ ๊ฒ์ ์๋๋ค', () => {
attendee.checkIn();
// this์ ๋น ๊ฐ์ฒด๋ฅผ ๋ฃ๊ณ
// counter.countIfCheckedIn์ ์คํํ๋ค.
counter.countIfCheckIn.call({}, attendee);
expect(counter.getCount()).toBe(1);
});
});
});
Conference.checkedInAttendeeCounter
์์ ์ ์ฐธ์กฐ๊ฐ์ self
๋ผ๋ ๋ณ์์ ๋ด๊ณ countIfCheckedIn
์์ this
๋์ self
๋ก ์ฐธ์กฐํ๋ฉด getCount
ํจ์๋ฅผ ํ์คํ ๋ฐ๋ผ๋ณผ ์ ์์ ๊ฒ์ด๋ค.
var Conference = Conference || {};
Conference.checkedInAttendeeCounter = function() {
var checkedInAttendees = 0;
var self = {
increment: function() {
checkedInAttendees += 1;
},
getCount: function() {
return checkedInAttendees;
},
countIfCheckedIn: function(attendee) {
if (attendee.isCheckedIn()) {
self.increment();
}
}
};
return self;
};
๐ ์ ๋ฆฌํ๊ธฐโ
ํ
์คํธ๋ฅผ ๋จผ์ ์์ฑํ๊ณ ์ฝ๋ฐฑ ํจ์๋ฅผ ์ฌ๋ฟ ํฌํจํ ์์ฝ๋๋ฅผ ๊ตฌํํ๋ฉด์ this
๊ฐ ๋ป๋ฐ์ ์๋ฑํ ์ฐธ์กฐ๋ฅผ ํ ์ ์๋ค๋ ์ฌ์ค์ ์์๋ค. ๋ํ, ์ต๋ช
์ฝ๋ฐฑ ํจ์๊ฐ ์ผ๋ง๋ ํ
์คํธํ๊ธฐ ์ด๋ ค์ด์ง, ์ฌ๊ธฐ์ ์ค์ฒฉ๊น์ง ๋ํ๋ฉด ๊ณจ์นซ๋ฉ์ด ์ฝ๋ฐฑ ํ์ด์ด ๋๊ณ ๋ง๋ค๋ ์ฌ์ค์ ๋ฐฐ์ ๋ค.