개요
레코드는 프로그래밍에서 데이터를 표현하는 구조이다.
연관된 데이터를 묶어 하나의 의미있는 단위를 만든다.
예시: 간단한 레코드 캡슐화하기
//이 상수는 레코드 구조로 사용되는 객체 const organization = {name:"애크미 구스베리", country:"GB"}; let result; result += `<h1>${organization.name}</h1>` //일기 organization.name = '새로운 이름' // 쓰기 |
//상수를 캡슐화하기, 이 게터는 임시 사용 function getRawDataOfOrganization() { return organization } let result; result += `<h1>${getRawDataOfOrganization().name}</h1>` //일기 getRawDataOfOrganization().name = '새로운 이름' // 쓰기 |
const organization = new Organization({ name: "애크미 구스베리", country: "GB" }); function getRawDataOfOrganization() {return organization._data;} function getOrganization(){return organization;} let result; result += `<h1>${getRawDataOfOrganization().name}</h1>` //일기 getRawDataOfOrganization().name = '새로운 이름' // 쓰기 //레코드를 캡슐화하는 목적은 변수를 통제하기 위함이다. //그러기 위해선 레코드를 클래스로 바꿔야한다. class Organization{ constructor(data){ this._data = data; } } |
const organization = new Organization({ name: "애크미 구스베리", country: "GB" }); // function getRawDataOfOrganization() {return organization._data;} 정상동작하면 제거 function getOrganization(){return organization;} let result; result += `<h1>${getOrganization().name}</h1>` //일기 getOrganization().name = '새로운 이름' // 쓰기 //게터, 세터 생성 후 사용하도록 코드 수정 class Organization{ constructor(data){ this._data = data; } set name(aString){this._data.name = aString;} get name(){return this._data.name;} } |
//현재 레코드를 그대로 받아 사용하고 있다. //레코드는 참조변수로 다루기에 캡슐화가 깨질 우려가 있다. 제거한다. //만일 _data를 그대로 쓸 것이라면 복제해서 써야한다. class Organization{ constructor(data){ this._name = data.name; this._country = data.country; } set name(aString){this._name = aString;} get name(){return this._name;} set _country(aString){this._country = aString;} get _country(){return this._country;} } |
예시: 중첩된 레코드 캡슐화하기
JSON 같은 여러 겹으로 중첩된 레코드에 대한 캡슐화
//중첩구조가 심할수록 데이터 일고/쓰기가 힘들어진다. customerData[customerID].usages[year][month] = amount;//쓰기 function compareUsage(customerID, laterYear, month){//읽기 const later = customerData[customerID].usages[laterYear][month]; const earlier = customerData[customerID].usages[laterYear-1][month]; return {laterAmount: later, change:later-earlier}; } |
getRawDataOfCustomers()[customerID].usages[year][month] = amount;//쓰기 function compareUsage(customerID, laterYear, month){//읽기 const later = getRawDataOfCustomers()[customerID].usages[laterYear][month]; const earlier = getRawDataOfCustomers()[customerID].usages[laterYear-1][month]; return {laterAmount: later, change:later-earlier}; } //변수 캡슐화부터 시작 function getRawDataOfCustomers(){return customerData;} function setRawDataOfCustomers(arg){customerData = arg;} |
function getCustomerData(){return customerData;} function getRawDataOfCustomers(){return customerData._data;}//기존 호환 function setRawDataOfCustomers(arg){customerData = new CustomerData(arg);} //데이터 구조를 표햔하는 클래스 정의 후 이를 반환하는 함수 만듦 class CustomerData{ constructor(data){ this._data = data; } } |
setUsage(customerID, year, month, amount);//쓰기 //데이터 구조 안으로 들어가는 세터 함수화 function setUsage(customerID, year, month, amount) { getRawDataOfCustomers()[customerID].usages[year][month] = amount; } |
getCustomerData().setUsage(customerID, year, month, amount);//쓰기 //클래스로 함수 옮기기 class CustomerData{ constructor(data){ this._data = data; } setUsage(customerID, year, month, amount) { getRawDataOfCustomers()[customerID].usages[year][month] = amount; } } |
class CustomerData{ constructor(data){ this._data = data; } setUsage(customerID, year, month, amount) { getRawDataOfCustomers()[customerID].usages[year][month] = amount; } //객체의 모든 필드가 불변이 아니라면, 캡슐화가 꺠질 수 있다. //그렇다고 customerData 를 사용하는 모든 코드를 확인 했는지 알 수가 없다. //복사본을 리턴해서 처리한다. get rawData(){ return _.cloneDeep(this._data); } } 프록시 구현으로 객체 수정 시 예외를 던지는 방법도 있다. |
function compareUsage(customerID, laterYear, month){//읽기 const later = getCustomerData().usage(customerID,laterYear,month); const earlier = getCustomerData().usage(customerID,laterYear-1,month); return {laterAmount: later, change:later-earlier}; } class CustomerData{ constructor(data){ this._data = data; } setUsage(customerID, year, month, amount) { getRawDataOfCustomers()[customerID].usages[year][month] = amount; } get rawData(){ return _.cloneDeep(this._data); } //읽기 처리 usage(customerID, year, month){ return this._data[customerID].usages[year][month]; } } |
//사용자가 데이터를 요청해 그 값을 수정하면 캡슐화가 깨질 수 있다. //복사본으로 처리하는 게 가장 간단한 방식 function compareUsage(customerID, laterYear, month){ const later = getCustomerData().rawData[customerID].usages[year][month]; const earlier = getCustomerData().rawData[customerID].usages[year][month]; return {laterAmount: later, change:later-earlier}; } |
요청 데이터를 리턴할 때 복사본으로 작업하면 간단하나 구조가 거대하면 성능이 감당 안될 수도 있다. 이럴 땐 프록시나 객체를 동결해 값을 비교해 틀어지면 예외를 던지도록 구현하는 방법도 있다.
다른 방법으로 레코드 캡슐화를 재귀적으로 하는 방식이 있다. 가장 손이 많이간다.
'IT책, 강의 > 리팩터링' 카테고리의 다른 글
07 - 캡슐화 - 기본형을 객체로 바꾸기 (0) | 2023.07.27 |
---|---|
07 - 캡슐화 - 컬렉션 캡슐화하기 (0) | 2023.07.26 |
06 - 기본적인 리펙터링 - 단계 쪼개기 (0) | 2023.07.24 |
06 - 기본적인 리펙터링 - 여러 함수를 변환 함수로 묶기 (0) | 2023.07.23 |
06 - 기본적인 리펙터링 - 여러 함수를 클래스로 묶기 (0) | 2023.07.22 |