developer tip

누수 추상화의 의미?

copycodes 2020. 10. 7. 07:48
반응형

누수 추상화의 의미?


"누수 추상화"라는 용어는 무엇을 의미합니까? (예를 들어 설명해주세요. 저는 종종 단순한 이론을 괴롭히는 데 어려움을 겪습니다.)


다음은 미트 스페이스 예입니다.

자동차에는 운전자를위한 추상화가 있습니다. 가장 순수한 형태에는 스티어링 휠, 액셀러레이터 및 브레이크가 있습니다. 이 추상화는 엔진, 캠, 타이밍 벨트, 스파크 플러그, 라디에이터 등 후드 아래에있는 내용에 대한 많은 세부 정보를 숨 깁니다.

이 추상화의 멋진 점은 사용자를 재교육하지 않고도 구현의 일부를 개선 된 부분으로 대체 할 수 있다는 것입니다. 분배기 캡을 전자 점화 장치로 교체하고 고정 캠을 가변 캠으로 교체한다고 가정 해 보겠습니다. 이러한 변화는 성능을 향상 시키지만 사용자는 여전히 휠로 조종하고 페달을 사용하여 시작 및 정지합니다.

실제로 매우 놀랍습니다. 16 세 또는 80 세는 내부 작동 방식에 대해 많이 알지 못해도이 복잡한 기계를 조작 할 수 있습니다!

그러나 누출이 있습니다. 변속기는 작은 누출입니다. 자동 변속기에서는 자동차가 기어를 전환 할 때 잠시 힘을 잃는 것을 느낄 수있는 반면 CVT에서는 부드러운 토크를 느낄 수 있습니다.

더 큰 누출도 있습니다. 엔진을 너무 빨리 회전하면 엔진이 손상 될 수 있습니다. 엔진 블록이 너무 차가워지면 자동차가 시동되지 않거나 성능이 저하 될 수 있습니다. 라디오, 헤드 라이트 및 AC를 동시에 크랭크하면 연비가 낮아지는 것을 볼 수 있습니다.


이는 추상화가 구현 세부 정보 중 일부를 노출하거나 추상화를 사용할 때 구현 세부 정보를 알고 있어야 함을 의미합니다. 이 용어는 2002 년경 Joel Spolsky에 기인합니다 . 자세한 내용은 wikipedia 기사 를 참조하십시오.

고전적인 예는 원격 파일을 로컬로 처리 할 수있는 네트워크 라이브러리입니다. 이 추상화를 사용하는 개발자는 네트워크 문제로 인해 로컬 파일이 수행하지 않는 방식으로 실패 할 수 있음을 알고 있어야합니다. 그런 다음 네트워크 라이브러리가 제공하는 추상화 외부의 오류를 특별히 처리하는 코드를 개발해야합니다.


Wikipedia는 이것에 대한 꽤 좋은 정의가지고 있습니다.

누출 된 추상화는 구현 된 추상화를 의미하며, 복잡성을 줄이거 나 숨기려는 목적으로 기본 세부 정보가 완전히 숨겨지지 않습니다.

즉, 소프트웨어의 경우 프로그램의 제한이나 부작용을 통해 기능의 구현 세부 사항을 관찰 할 수 있습니다.

빠른 예는 C # / VB.Net 클로저와 ref / out 매개 변수를 캡처 할 수없는 경우입니다. 캡처 할 수없는 이유는 리프팅 프로세스가 발생하는 방식에 대한 구현 세부 사항 때문입니다. 더 나은 방법이 있다고 말하는 것은 아닙니다.


다음은 .NET 개발자에게 친숙한 예입니다. ASP.NET의 Page클래스는 HTTP 작업, 특히 양식 데이터 관리의 세부 정보를 숨기려고 시도하므로 개발자가 게시 된 값을 처리 할 필요가 없습니다 (양식 값을 서버에 자동으로 매핑하기 때문). 통제 수단).

그러나 가장 기본적인 사용 시나리오를 벗어나면 Page추상화가 누출되기 시작하고 클래스의 구현 세부 사항을 이해하지 않으면 페이지 작업이 어려워집니다.

한 가지 일반적인 예는 페이지에 컨트롤을 동적으로 추가하는 것입니다. 동적으로 추가 된 컨트롤의 값은 적절한 때에 추가하지 않는 한 매핑되지 않습니다 . 기본 엔진이 들어오는 양식 값을 적절한 컨트롤에 매핑하기 전에. 그것을 배워야 할 때 추상화가 유출되었습니다 .


글쎄요, 그것은 중요하지 않지만 순전히 이론적 인 것입니다.

우리는 사물을 더 쉽게 이해할 수 있도록 추상화를 사용합니다. 개별 항목 인 정렬 된 문자 집합을 처리하고 있다는 사실을 숨기기 위해 일부 언어의 문자열 클래스에서 작업 할 수 있습니다. 나는 숫자를 다루고 있다는 사실을 숨기기 위해 순서가 지정된 문자 집합을 처리합니다. 나는 1과 0을 다루고 있다는 사실을 숨기기 위해 숫자를 다룬다.

새는 추상화는 숨기려는 세부 사항을 숨기지 않는 추상화입니다. Java 또는 .NET의 5 자 문자열에서 string.Length를 호출하면 해당 언어가 문자를 호출하는 것이 실제로 1 또는 1 또는 1을 나타낼 수있는 UTF-16 데이터 포인트 인 구현 세부 사항 때문에 5에서 10까지의 응답을 얻을 수 있습니다. 문자의 .5. 추상화가 유출되었습니다. 누출이 없다는 것은 길이를 찾는 것이 더 많은 저장 공간 (실제 길이를 저장하기 위해)이 필요하거나 O (1)에서 O (n)으로 변경 (실제 길이가 무엇인지 알아 내기 위해)된다는 것을 의미합니다. 내가 진짜 대답에 관심이 있다면 (종종 당신은 그렇지 않다) 실제로 무슨 일이 일어나고 있는지에 대한 지식을 연구해야합니다.

더 논쟁의 여지가있는 경우는 메서드 나 속성이 내부 작업에 들어갈 수있게 해주는 경우에 발생합니다. 추상화 누출이든 더 낮은 수준의 추상화로 이동하는 잘 정의 된 방법은 때때로 사람들이 동의하지 않는 문제 일 수 있습니다.


RPC를 사용하여 예제를 제공하는 맥락에서 계속하겠습니다.

RPC의 이상적인 세계에서 원격 프로 시저 호출은 로컬 프로 시저 호출처럼 보여야합니다 (또는 이야기가 진행됨). 여기에는 그들이 호출 할 때하는 프로그래머 완전히 투명해야 SomeObject.someFunction()하는 경우가 아무 생각이 없다 SomeObject(또는 someFunction그 문제에 대해) 로컬로 저장되고 실행 또는 원격으로 저장되고 실행됩니다. 이론은 이것이 프로그래밍을 더 간단하게 만든다는 것입니다.

(세계에서 가장 느린 통역 언어를 사용하는 경우에도) 로컬 함수 호출과 다음 사이에는 큰 차이가 있기 때문에 현실은 다릅니다.

  • 프록시 개체를 통해 호출
  • 매개 변수 직렬화
  • 네트워크 연결 만들기 (아직 설정되지 않은 경우)
  • 원격 프록시로 데이터 전송
  • 원격 프록시가 데이터를 복원하고 사용자를 대신하여 원격 기능을 호출하도록합니다.
  • 반환 값 직렬화
  • 반환 값을 로컬 프록시로 전송
  • 직렬화 된 데이터 재 조립
  • 원격 함수에서 응답 반환

시간 만에 그것은 약 3 차 (또는 그 이상!)의 크기 차이입니다. 이러한 3 배 이상의 크기는 RPC를 실수로 실제 함수 호출로 처음 처리 할 때 프로 시저 호출의 추상화를 확실히 유출시키는 성능에 큰 차이를 만들 것입니다. 또한 코드에서 심각한 문제를 제외하고 실제 함수 호출에는 구현 버그 외에 실패 지점이 거의 없습니다. RPC 호출에는 일반 로컬 호출에서 기대할 수있는 것보다 더 많은 실패 사례가 발생하는 다음과 같은 가능한 문제가 모두 있습니다.

  • 로컬 프록시를 인스턴스화하지 못할 수 있습니다.
  • 원격 프록시를 인스턴스화하지 못할 수 있습니다.
  • 프록시가 연결되지 않을 수 있습니다.
  • 당신이 보내는 매개 변수는 그것을 손상시키지 않거나 전혀 만들지 않을 수 있습니다
  • 원격이 보내는 반환 값은 손상되지 않거나 전혀 만들 수 없습니다.

이제 "로컬 함수 호출과 같은"RPC 호출에는 로컬 함수 호출을 수행 할 때 다룰 필요가없는 추가 실패 조건이 있습니다. 추상화가 다시 유출되었습니다.

결국 RPC는 성공할 때와 실패 할 때 모든 수준에서 체처럼 누출되기 때문에 잘못된 추상화입니다.


의 예 장고는 다 대다 예를 ORM :

다 대다 속성에 Publication 객체를 추가하기 전에 기본 아티클 객체 a1을 .save ()해야한다는 것을 샘플 API 사용에서 확인하십시오. 다 대다 속성을 업데이트하면 기본 데이터베이스에 즉시 저장되는 반면, 단일 속성 업데이트는 .save ()가 호출 될 때까지 db에 반영되지 않습니다.

The abstraction is that we are working with an object graph, where single-value attributes and mult-value attributes are just attributes. But the implementation as a relational database backed data store leaks... as the integrity system of the RDBS appears through the thin veneer of an object interface.


The fact that at some point, which will guided by your scale and execution, you will be needed to get familiar with the implementation details of your abstraction framework in order to understand why it behave that way it behave.

For example, consider this SQL query:

SELECT id, first_name, last_name, age, subject FROM student_details;

And it's alternative:

SELECT * FROM student_details;

Now, they do look like a logically equivalent solutions, but the performance of the first one is better due the individual column names specification.

It's a trivial example but eventually it comes back to Joel Spolsky quote:

All non-trivial abstractions, to some degree, are leaky.

At some point, when you will reach a certain scale in your operation, you will want to optimize the way your DB (SQL) works. To do it, you will need to know the way relational databases works. It was abstracted to you in the beginning, but it's leaky. You need to learn it at some point.


Assume, we have the following code in a library:

Object[] fetchDeviceColorAndModel(String serialNumberOfDevice)
{
    //fetch Device Color and Device Model from DB.
    //create new Object[] and set 0th field with color and 1st field with model value. 
}

When the consumer calls the API, they get an Object[]. The consumer has to understand that the first field of the object array has color value and second field is the model value. Here the abstraction has leaked from library to the consumer code.

One of the solutions is to return an object which encapsulates Model and Color of the Device. The consumer can call that object to get the model and color value.

DeviceColorAndModel fetchDeviceColorAndModel(String serialNumberOfTheDevice)
{
    //fetch Device Color and Device Model from DB.
    return new DeviceColorAndModel(color, model);
}

Leaky abstraction is all about encapsulating state. very simple example of leaky abstraction:

$currentTime = new DateTime();

$bankAccount1->setLastRefresh($currentTime);
$bankAccount2->setLastRefresh($currentTime);
$currentTime->setTimestamp($aTimestamp);

class BankAccount {
    // ...

    public function setLastRefresh(DateTimeImmutable $lastRefresh)
    {
        $this->lastRefresh = $lastRefresh;
    } }

and the right way(not leaky abstraction):

class BankAccount
{
    // ...

    public function setLastRefresh(DateTime $lastRefresh)
    {
        $this->lastRefresh = clone $lastRefresh;
    }
}

more description here.

참고URL : https://stackoverflow.com/questions/3883006/meaning-of-leaky-abstraction

반응형