가장 단순한 특성 쇼케이스

9890 단어 javarust

소개



구현에서 인터페이스를 분리할 수 있는 것이 편리합니다. 그러나 종종 충분하지 않습니다. 이 글에서는 인터페이스 클래스 패러다임 제한의 가장 간단한 예에 대해 이야기하고 싶습니다.

Java 또는 기타 고전적인 OOP 언어로 Size 클래스를 설계해야 한다면 어떻게 될까요? 특히 건설(공장)과 세터(있는 경우)에는 많은 변형이 있지만 모든 단일 구현에서 변경되지 않은 한 가지는 두 개의 숫자입니다.

// Java

class Size {
    private int mWidth;
    private int mHeight;

    public Size(int width, int height) {
        if (width < 0 || height < 0) {
            throw new IllegalArgumentException();
        }
        mWidth = width;
        mHeight = height;
    }

    public int width() {
        return mWidth;
    }

    public int height() {
        return mHeight;
    }

    public int area() {
        return width() * height();
    }
}


모든 엔지니어가 이와 같은 것을 몇 번 작성해야 했지만 데이터 관점에서 이것이 당신을 귀찮게 한 적이 없었습니까? 한 쌍의 숫자가 갑자기 Size 가 될 때마다 이상하게 생각됩니다. 한 쌍의 숫자는 무엇이든 될 수 있지만 데이터는 구현에서 분리할 수 없기 때문에 Size , PointComplexNumber와 같은 클래스를 만듭니다. 모두 정확히 두 개의 숫자를 포함합니다. 데이터 관점에서 다르지 않습니다. 유형.

물론 Java에서는 인터페이스의 관점에서 생각해야 하기 때문에 이렇게 합니다. 구현에는 단순히 인터페이스 계약을 이행하는 데 필요한 모든 멤버 변수가 포함됩니다. 여기서 우리가 지불하는 대가는 스택에 완벽하게 미세한 한 쌍의 숫자가 있더라도 Size를 생성해야 한다는 것입니다.Size 의 경우 큰 희생은 아닌 것 같습니다. 그러나 일단 유형의 구조, 유형에 수행되는 변환 및 배선이 더 복잡해지면 완벽하게 미세한 데이터를 사용할 수 없다는 것이 다소 제한적일 수 있습니다. 특히 데이터가 큰 경우에는 더욱 그렇습니다.

해결책



경우에 따라 다른 방식으로 데이터를 처리하는 관용적인 방법이 있다면 어떨까요? 이것이 바로 특성입니다. 특정 데이터 유형을 해석하기 위해 미리 정의된 방법입니다. Rust에서 트레잇으로 비슷한 일을 할 수 있는 방법을 살펴보겠습니다.

첫째, 여기에서 데이터 유형을 선언할 필요조차 없습니다. 대신 튜플을 사용할 수 있습니다. 크기로 취급하기에 적합한 변수를 선언하는 방법은 다음과 같습니다.

// Rust

let s = (2, 3);


그런 다음 특성을 선언합니다. area() 함수를 바로 정의합니다. 면적 계산이 width()height() 함수 구현 세부 사항에 의존하지 않기 때문입니다.

// Rust

trait Size {
    fn width(&self) -> u32;

    fn height(&self) -> u32;

    fn area(&self) -> u32 {
        return self.width() * self.height();
    }
}


남은 일은 두 정수(u32, u32)의 튜플을 Size로 해석하도록 정의하는 것입니다.

// Rust

impl Size for (u32, u32) {
    fn width(&self) -> u32 {
        return self.0;
    }

    fn height(&self) -> u32 {
        return self.1;
    }
}


Size 의 조각에 대한 u32 특성을 구현하는 것은 간단합니다.

// Rust

impl Size for [u32; 2] {
    fn width(&self) -> u32 {
        return self[0];
    }

    fn height(&self) -> u32 {
        return self[1];
    }
}


언어에 이미 존재하는 두 가지 기본 데이터 유형에 대한 특성Size을 방금 구현했습니다. 이제 이러한 구현을 호출해 보겠습니다.

// Rust

fn foo() {
    let tuple = (2, 3);

    // Automatic trait resolution
    println!("Area: {}", tuple.area());

    // Explicitly choose trait implementation
    println!("Area: {}", Size::area(&tuple));


    let array = [4, 5];

    // Automatic trait resolution
    println!("Area: {}", array.area());

    // Explicitly choose trait implementation
    println!("Area: {}", Size::area(&array));
}


생각



인터페이스를 따로 선언해도 구현과 함께 데이터를 붙일 필요가 없고 중복 데이터 타입 정의 없이 유용한 코드 작성이 가능하다. 특성을 사용하면 동일한 데이터를 경우에 따라 다르게 해석할 수 있으며, 이는 좋은 디자인을 추구하는 데 있어 모든 것을 변경합니다. 특성은 때때로 객체 지향 프로그래밍의 개념으로 언급되지만 제 생각에는 오해의 소지가 있습니다. 완전한 특성은 Rust와 같이 데이터 지향 설계에 기대는 언어에서만 찾을 수 있습니다.

좋은 웹페이지 즐겨찾기