객체 지향 프로그래밍의 가장 중요한 원칙 중 하나는 내부 인터페이스와 외부 인터페이스를 구분하는 것입니다.
이는 "hello world" 앱보다 더 복잡한 것을 개발할 때 "필수" 관행입니다.
이를 이해하기 위해 개발에서 벗어나 현실 세계로 눈을 돌려보자.
일반적으로 우리가 사용하는 장치는 상당히 복잡합니다. 그러나 내부 인터페이스와 외부 인터페이스를 구분하면 문제 없이 사용할 수 있습니다.
예를 들어, 커피 머신. 겉모습은 간단합니다. 버튼, 디스플레이, 구멍 몇 개... 그리고 결과는 당연합니다 – 훌륭한 커피입니다! :)
하지만 내부는… (수리 설명서에 있는 사진)
많은 세부 사항. 하지만 우리는 아무것도 모르고도 사용할 수 있습니다.
커피머신은 꽤 믿을 만하지 않나요? 수년 동안 사용할 수 있으며, 문제가 발생한 경우에만 수리를 위해 가져오세요.
커피머신의 신뢰성과 단순함의 비결 – 모든 세부 사항이 잘 조정되어 내부에 숨겨져 있습니다 .
커피 머신에서 보호 커버를 제거하면 사용이 훨씬 더 복잡해지고(어디를 눌러야 합니까?) 위험합니다(감전될 수 있음).
앞으로 살펴보겠지만 프로그래밍에서 객체는 커피 머신과 같습니다.
그러나 내부 세부 사항을 숨기기 위해 보호 덮개가 아닌 언어 및 규칙의 특수 구문을 사용합니다.
객체 지향 프로그래밍에서 속성과 메서드는 두 그룹으로 나뉩니다.
내부 인터페이스 – 클래스의 다른 메서드에서 액세스할 수 있지만 외부에서는 액세스할 수 없는 메서드 및 속성입니다.
외부 인터페이스 - 클래스 외부에서도 액세스할 수 있는 메서드 및 속성입니다.
커피 머신에 비유를 계속하면 내부에 숨겨진 것(보일러 튜브, 가열 요소 등)은 내부 인터페이스입니다.
내부 인터페이스는 객체가 작동하는 데 사용되며 세부 사항은 서로를 사용합니다. 예를 들어 보일러 튜브가 발열체에 부착됩니다.
하지만 외부에서는 커피 머신이 보호 커버로 닫혀 있어 누구도 접근할 수 없습니다. 세부정보는 숨겨져 있어 접근할 수 없습니다. 외부 인터페이스를 통해 해당 기능을 사용할 수 있습니다.
따라서 객체를 사용하기 위해 필요한 것은 외부 인터페이스를 아는 것뿐입니다. 우리는 그것이 내부에서 어떻게 작동하는지 전혀 알지 못할 수도 있습니다. 그것은 훌륭합니다.
일반적인 소개였습니다.
JavaScript에는 두 가지 유형의 개체 필드(속성 및 메서드)가 있습니다.
공개: 어디서나 접근 가능. 이는 외부 인터페이스를 구성합니다. 지금까지 우리는 공용 속성과 메서드만 사용했습니다.
비공개: 클래스 내부에서만 접근 가능합니다. 이는 내부 인터페이스용입니다.
다른 많은 언어에는 "보호된" 필드도 있습니다. 즉, 클래스 내부와 이를 확장하는 필드에서만 액세스할 수 있습니다(비공개와 같지만 상속 클래스에서도 액세스 가능). 내부 인터페이스에도 유용합니다. 우리는 일반적으로 상속 클래스가 이에 액세스하기를 원하기 때문에 어떤 의미에서는 비공개 클래스보다 더 널리 퍼져 있습니다.
보호 필드는 언어 수준에서 JavaScript로 구현되지 않지만 실제로는 매우 편리하므로 에뮬레이션됩니다.
이제 이러한 모든 유형의 속성을 갖춘 JavaScript로 커피 머신을 만들어 보겠습니다. 커피 머신에는 많은 세부 사항이 있으므로 단순하게 유지하기 위해 모델링하지는 않습니다(할 수는 있지만).
먼저 간단한 커피 머신 수업을 만들어 보겠습니다.
클래스 커피 머신 { 물량 = 0; // 내부의 물의 양 생성자(전원) { this.power = 힘; Alert( `커피 머신을 만들었습니다. 전원: ${power}` ); } } // 커피 머신 생성 let CoffeeMachine = new CoffeeMachine(100); // 물 추가 CoffeeMachine.waterAmount = 200;
현재 waterAmount
및 power
속성은 공개되어 있습니다. 외부에서 어떤 값으로든 쉽게 가져오거나 설정할 수 있습니다.
더 효과적으로 제어할 수 있도록 waterAmount
속성을 protected로 변경해 보겠습니다. 예를 들어, 우리는 누구도 이 값을 0 이하로 설정하는 것을 원하지 않습니다.
보호된 속성에는 일반적으로 밑줄 _
접두사가 붙습니다.
이는 언어 수준에서는 적용되지 않지만 이러한 속성과 메서드는 외부에서 액세스해서는 안 된다는 프로그래머 사이의 잘 알려진 규칙이 있습니다.
따라서 우리의 속성은 _waterAmount
라고 불릴 것입니다:
클래스 커피 머신 { _waterAmount = 0; waterAmount(값) 설정 { if (값 < 0) { 값 = 0; } this._waterAmount = 값; } waterAmount()를 얻습니다. this._waterAmount를 반환합니다. } 생성자(전원) { this._power = 힘; } } // 커피 머신 생성 let CoffeeMachine = new CoffeeMachine(100); // 물 추가 CoffeeMachine.waterAmount = -10; // _waterAmount는 -10이 아닌 0이 됩니다.
현재는 출입이 통제되고 있어 수량을 0 이하로 설정하는 것은 불가능해졌습니다.
power
속성은 읽기 전용으로 만들어 보겠습니다. 속성은 생성 시에만 설정되고 수정되지 않아야 하는 경우가 있습니다.
커피 머신의 경우도 마찬가지입니다. 전원은 절대 변하지 않습니다.
그렇게 하려면 getter만 만들면 되고 setter는 만들 필요가 없습니다.
클래스 커피 머신 { // ... 생성자(전원) { this._power = 힘; } 힘을 얻으십시오() { return this._power; } } // 커피 머신 생성 let CoffeeMachine = new CoffeeMachine(100); Alert(`전력: ${coffeeMachine.power}W`); // 전력: 100W 커피머신.파워 = 25; // 오류(설정자 없음)
게터/세터 함수
여기서는 getter/setter 구문을 사용했습니다.
그러나 대부분의 경우 다음과 같이 get.../set...
기능이 선호됩니다.
클래스 커피 머신 { _waterAmount = 0; setWaterAmount(값) { (값 < 0) 값 = 0인 경우; this._waterAmount = 값; } getWaterAmount() { this._waterAmount를 반환합니다. } } new CoffeeMachine().setWaterAmount(100);
조금 더 길어 보이지만 기능은 더 유연합니다. 그들은 여러 인수를 받아들일 수 있습니다(지금은 필요하지 않더라도).
반면에 get/set 구문은 더 짧기 때문에 궁극적으로 엄격한 규칙은 없으며 결정하는 것은 사용자의 몫입니다.
보호된 필드는 상속됩니다.
class MegaMachine extends CoffeeMachine
경우 새 클래스의 메서드에서 this._waterAmount
또는 this._power
에 액세스하는 것을 방해하는 것은 없습니다.
따라서 보호된 필드는 자연스럽게 상속 가능합니다. 아래에서 볼 개인과 다릅니다.
최근 추가된 내용
이것은 최근에 언어에 추가된 것입니다. JavaScript 엔진에서는 지원되지 않거나 아직 부분적으로 지원되므로 폴리필이 필요합니다.
개인 속성과 메서드에 대한 언어 수준 지원을 제공하는 완성된 JavaScript 제안이 거의 표준에 있습니다.
비공개는 #
으로 시작해야 합니다. 클래스 내부에서만 접근이 가능합니다.
예를 들어, 여기에 개인 #waterLimit
속성과 물 확인 개인 메서드 #fixWaterAmount
가 있습니다.
클래스 커피 머신 { #waterLimit = 200; #fixWaterAmount(값) { (값 < 0)인 경우 0을 반환합니다. if (값 > this.#waterLimit) return this.#waterLimit; } setWaterAmount(값) { this.#waterLimit = this.#fixWaterAmount(값); } } let CoffeeMachine = new CoffeeMachine(); // 클래스 외부에서는 비공개 항목에 접근할 수 없습니다. CoffeeMachine.#fixWaterAmount(123); // 오류 CoffeeMachine.#waterLimit = 1000; // 오류
언어 수준에서 #
해당 필드가 비공개임을 나타내는 특수 기호입니다. 외부나 상속 클래스에서는 접근할 수 없습니다.
비공개 필드는 공개 필드와 충돌하지 않습니다. 비공개 #waterAmount
필드와 공공 waterAmount
필드를 동시에 가질 수 있습니다.
예를 들어, waterAmount
#waterAmount
에 대한 접근자로 만들어 보겠습니다.
클래스 커피 머신 { #물량 = 0; waterAmount()를 얻습니다. return this.#waterAmount; } waterAmount(값) 설정 { if (값 < 0) 값 = 0; this.#waterAmount = 값; } } machine = new CoffeeMachine()을 두십시오. machine.waterAmount = 100; 경고(machine.#waterAmount); // 오류
보호된 필드와 달리 비공개 필드는 언어 자체에 의해 적용됩니다. 그것은 좋은 일입니다.
그러나 CoffeeMachine
에서 상속받는 경우에는 #waterAmount
에 직접 액세스할 수 없습니다. waterAmount
getter/setter에 의존해야 합니다.
MegaCoffeeMachine 클래스는 CoffeeMachine {을 확장합니다. 방법() { 경고( this.#waterAmount ); // 오류: CoffeeMachine에서만 접근할 수 있습니다. } }
많은 시나리오에서 이러한 제한은 너무 심각합니다. CoffeeMachine
을 확장하면 내부에 액세스할 수 있는 정당한 이유가 있을 수 있습니다. 이것이 보호 필드가 언어 구문에서 지원되지 않더라도 더 자주 사용되는 이유입니다.
이[이름]은(는) 비공개 필드를 사용할 수 없습니다.
비공개 필드는 특별합니다.
아시다시피 일반적으로 this[name]
사용하여 필드에 액세스할 수 있습니다.
클래스 사용자 { ... 안녕하세요() { let fieldName = "이름"; Alert(`안녕하세요, ${this[fieldName]}`); } }
불가능한 비공개 필드에서는 this['#name']
작동하지 않습니다. 이는 개인 정보 보호를 보장하기 위한 구문 제한 사항입니다.
OOP 측면에서 내부 인터페이스와 외부 인터페이스를 구분하는 것을 캡슐화라고 합니다.
다음과 같은 이점을 제공합니다.
사용자가 발에 총을 쏘지 않도록 보호
커피 머신을 사용하는 개발자 팀이 있다고 상상해 보세요. "Best CoffeeMachine" 회사에서 만든 제품이고 잘 작동하는데 보호 커버가 벗겨져 있었습니다. 따라서 내부 인터페이스가 노출됩니다.
모든 개발자는 문명화되어 있습니다. 의도한 대로 커피 머신을 사용합니다. 하지만 그들 중 한 명인 John은 자신이 가장 똑똑한 사람이라고 판단하고 커피 머신 내부를 약간 수정했습니다. 그래서 커피머신은 이틀 뒤에 고장이 났습니다.
그것은 분명히 John의 잘못이 아니라 보호 덮개를 제거하고 John이 조작하도록 놔둔 사람의 잘못입니다.
프로그래밍에서도 마찬가지입니다. 클래스 사용자가 외부에서 변경하려는 의도가 아닌 항목을 변경하는 경우 결과는 예측할 수 없습니다.
지원 가능
프로그래밍 상황은 실제 커피 머신보다 더 복잡합니다. 왜냐하면 우리는 커피 머신을 한 번만 구매하는 것이 아니기 때문입니다. 코드는 지속적으로 개발되고 개선됩니다.
내부 인터페이스를 엄격하게 구분하면 클래스 개발자는 사용자에게 알리지 않고도 내부 속성과 메서드를 자유롭게 변경할 수 있습니다.
이러한 클래스의 개발자라면 전용 메서드의 이름을 안전하게 바꿀 수 있고 해당 매개 변수를 변경하거나 제거할 수도 있다는 점을 아는 것이 좋습니다. 외부 코드가 종속되지 않기 때문입니다.
사용자 입장에서는 새 버전이 나오면 내부적으로는 전면적인 점검이 될 수 있지만, 외부 인터페이스가 동일하다면 업그레이드는 여전히 간단합니다.
복잡성 숨기기
사람들은 단순한 것을 사용하는 것을 좋아합니다. 적어도 외부에서는. 안에 있는 것은 다릅니다.
프로그래머도 예외는 아닙니다.
구현 세부 사항이 숨겨져 있고 간단하고 잘 문서화된 외부 인터페이스를 사용할 수 있으면 항상 편리합니다.
내부 인터페이스를 숨기기 위해 보호 또는 개인 속성을 사용합니다.
보호된 필드는 _
로 시작합니다. 이는 언어 수준에서 시행되지 않는 잘 알려진 규칙입니다. 프로그래머는 해당 클래스와 이를 상속하는 클래스에서 _
로 시작하는 필드에만 액세스해야 합니다.
비공개 필드는 #
으로 시작합니다. JavaScript는 클래스 내부에서만 접근할 수 있도록 해줍니다.
현재 비공개 필드는 브라우저에서 잘 지원되지 않지만 폴리필이 가능합니다.