트레이트 (컴퓨터 프로그래밍)

컴퓨터 프로그래밍에서 트레이트(trait)는 메서드 집합을 나타내는 프로그래밍 언어 개념으로, 클래스의 기능을 확장하는 데 사용될 수 있다.

근거
객체 지향 프로그래밍에서, 행동은 서로 관련 없는 클래스들 사이에서 공유되는 경우가 있다. 예를 들어, 관련 없는 많은 클래스들이 객체를 직렬화하여 JSON으로 만드는 메서드를 가질 수 있다. 역사적으로, 이 동작이 필요한 모든 클래스에서 코드를 중복하지 않고 이 문제를 해결하기 위한 여러 접근 방식이 있었다. 다른 접근 방식으로는 다중 상속과 믹스인이 있지만, 이들은 단점이 있다. 믹스인이 적용되는 순서가 변경되거나 부모 클래스나 믹스인에 새로운 메서드가 추가되면 코드의 동작이 예기치 않게 변경될 수 있다.

트레이트는 클래스가 트레이트를 사용하여 원하는 동작을 얻을 수 있게 함으로써 이러한 문제를 해결한다. 클래스가 하나 이상의 트레이트를 사용하는 경우, 트레이트가 사용되는 순서는 중요하지 않다. 트레이트가 제공하는 메서드는 클래스의 데이터에 직접 접근할 수 있다.

특징
트레이트는 프로토콜(인터페이스)과 믹스인의 측면을 결합한다. 인터페이스와 마찬가지로 트레이트는 하나 이상의 메서드 시그니처를 정의하며, 이를 구현하는 클래스는 구현을 제공해야 한다. 믹스인과 마찬가지로 트레이트는 구현하는 클래스에 추가적인 동작을 제공한다.

다른 트레이트가 제공하는 메서드 사이에 이름 충돌이 발생하는 경우, 프로그래머는 클래스에서 사용할 메서드를 명시적으로 구분해야 한다. 따라서 다중 상속의 다이아몬드 문제를 수동으로 해결한다. 이는 객체 지향 프로그래밍의 다른 구성 방법과는 다른데, 다른 방법에서는 충돌하는 이름이 스코프 규칙에 의해 자동으로 해결된다.

트레이트로 수행할 수 있는 작업은 다음과 같다.
 대칭 합: 두 개의 서로 다른 트레이트를 병합하여 새로운 트레이트를 생성하는 연산
 재정의 (또는 비대칭 합): 기존 트레이트에 메서드를 추가하여 새로운 트레이트를 형성하는 연산으로, 일부 메서드를 재정의할 수 있다.
 별칭: 기존 메서드에 새로운 이름을 추가하여 새로운 트레이트를 생성하는 연산
 제외: 기존 트레이트에서 메서드를 제거하여 새로운 트레이트를 형성하는 연산. (이것을 별칭 연산과 결합하면 얕은 이름 변경 연산이 된다).

트레이트에서 메서드가 제외되면 해당 메서드는 트레이트를 사용하는 클래스 또는 해당 클래스의 부모 클래스에 의해 제공되어야 한다. 이는 트레이트가 제공하는 다른 메서드가 제외된 메서드를 호출할 수 있기 때문이다.

트레이트 구성은 교환적 (즉, 트레이트 A와 B가 주어졌을 때, A + B는 B + A와 동등하다) 이고 결합적이다 (즉, 트레이트 A, B, C가 주어졌을 때, (A + B) + C는 A + (B + C)와 동등하다).

제한 사항
트레이트는 다른 많은 대안보다 상당한 이점을 제공하지만, 자체적인 제한 사항도 있다.

 필수 메서드 
트레이트가 사용하는 클래스에 특정 메서드를 제공하도록 요구하는 경우, 트레이트는 해당 메서드가 트레이트의 요구 사항과 의미론적으로 동일한지 알 수 없다. 펄과 같은 일부 동적 언어의 경우, 필수 메서드는 전체 메서드 시그니처가 아닌 메서드 이름으로만 식별될 수 있어 필수 메서드가 적절한지 보장하기가 더 어렵다.

 메서드 제외 
트레이트에서 메서드가 제외되면 해당 메서드는 트레이트의 다른 메서드가 호출할 수 있으므로 트레이트의 '필수' 메서드가 된다.

지원되는 언어
트레이트는 원래 셀프 프로그래밍 언어에서 나왔으며 다음 프로그래밍 언어에서 지원된다.

 AmbientTalk: 셀프 트레이트(객체 기반 다중 상속)와 스몰토크의 스퀵 트레이트(프로그래머에 의한 명시적인 트레이트 구성 요구)의 속성을 결합한다. 트레이트 내부에 상태를 활성화하기 위해 상태 저장 및 고정 가능한 트레이트에 대한 연구를 기반으로 하며, 이는 초기 정의에서는 허용되지 않았다.
 C 샤프: 버전 8.0부터 C#은 트레이트의 일부 속성을 가진 기본 인터페이스 메서드를 지원한다.
 C++: 표준 템플릿 라이브러리 및 C++ 표준 라이브러리에서 컨테이너 클래스를 지원하는 데 사용되며 부스트 TypeTraits 라이브러리에서도 사용된다.
 컬: 믹스인으로서의 추상 클래스는 메서드 구현을 허용하므로 다른 이름으로 트레이트를 구성한다.
 포트리스
 그루비: 버전 2.3부터
 하스켈: 하스켈에서 트레이트는 타입 클래스로 알려져 있다.
 Haxe: 버전 2.4.0부터. 매뉴얼에서는 Static Extension이라고 부르며, using 키워드를 사용한다.
 자바: 버전 8부터 자바는 트레이트의 일부 속성을 가진 기본 메서드를 지원한다.
 자바스크립트: 트레이트는 함수와 위임을 통해 구현될 수 있으며 트레이트를 제공하는 라이브러리를 통해서도 구현될 수 있다.
 줄리아: 여러 패키지가 트레이트를 구현한다. 예:
 코틀린: M12부터 트레이트를 인터페이스라고 부른다.
 라쏘
 모조: 버전 0.6.0부터
 OCaml: 트레이트는 다양한 언어 기능을 사용하여 구현할 수 있다. 모듈 및 모듈 타입 포함, 펑터 및 펑터 타입, 클래스 및 클래스 타입 상속 등.
 펄: 역할이라고 불리며, Moose, Role::Tiny 및 Role::Basic과 같은 펄 라이브러리에서 구현된다. 역할은 자매 언어인 라쿠의 일부이다. Corinna OOP Proposal이 수용됨에 따라 펄은 현대 OOP 시스템의 일부로 언어에 내장된 역할을 갖게 될 것이다.
 PHP: 버전 5.4부터 PHP는 사용자가 여러 (트레이트-)클래스에서 "상속"할 수 있는 기능을 제공하는 템플릿을 지정할 수 있도록 하여 의사 다중 상속과 같이 동작한다.
 파이썬: 타사 라이브러리를 통해, 또는 고차 믹스인 클래스를 통해
 래킷: 라이브러리로서 트레이트를 지원하며 매크로, 구조체 및 일급 클래스를 사용하여 구현한다.
 루비: 모듈 믹스인을 사용하여 트레이트를 구현할 수 있다.
 러스트
 스칼라 trait 키워드로 트레이트가 내장 지원된다.
 스몰토크: 트레이트는 스몰토크의 두 가지 방언인 스퀵과 파로에서 구현된다.
 스위프트: 프로토콜 확장을 통해 트레이트를 구현할 수 있다.

 예시 

 C# 
C# 8.0에서는 인터페이스의 멤버로 구현을 정의할 수 있다.using System;

namespace CSharp8NewFeatures;

interface ILogger
{
    // Traditional interface methods
    void Log(string message);
    void LogError(Exception exception);

    // Default interface method
    void LogWarning(string message)
    {
        Console.WriteLine(message);
    }
}

class Logger : ILogger
{
    public void Log(string message)
    {
        Console.WriteLine(message);
    }

    public void LogError(Exception exception)
    {
        Console.WriteLine(exception.ToString());
    }
}

public class Program
{
    static void Main(string[] args)
    {
        ILogger logger = new Logger();

        logger.LogWarning("Some warning message");
    }
}

 PHP 
이 예시는 트레이트를 사용하여 다른 클래스를 확장하는 방법을 보여준다.
// The template
trait TSingleton
{
    private static $_instance = null;

    private function __construct() {} // Must have private default constructor and be aware not to open it in the class

    public static function getInstance()
    {
        if (null === self::$_instance) {
            self::$_instance = new self();
        }

        return self::$_instance;
    }
}

class FrontController
{
    use TSingleton;
}

// Can also be used in already extended classes
class WebSite extends SomeClass
{
    use TSingleton;
}

이는 다중 상속의 측면을 시뮬레이션할 수 있게 한다.
trait TBounding
{
    public $x, $y, $width, $height;
}

trait TMoveable
{
    public function moveTo($x, $y)
    {
        // …
    }
}

trait TResizeable
{
    public function resize($newWidth, $newHeight)
    {
        // …
    }
}

class Rectangle
{
    use TBounding, TMoveable, TResizeable;

    public function fillColor($color)
    {
        // …
    }
}

 Rust 
러스트의 트레이트는 타입이 구현해야 하는 메서드 집합을 선언한다. 러스트 컴파일러는 트레이트가 명시적으로 작성되도록 요구하며, 이는 러스트에서 제네릭의 안전성을 보장한다.

// type T must have the "Ord" trait
// so that ">" and "<" operations can be done
fn max<T: Ord>(a: &[T]) -> Option<&T> {
    let mut result = a.first()?;
    for n in a {
        if *n > *result {
            result = &n;
        }
    }
    Some(result)
}

Debug 및 Ord와 같은 트레이트의 지루하고 반복적인 구현을 단순화하기 위해 derive 매크로를 사용하여 컴파일러가 특정 구현을 자동으로 생성하도록 요청할 수 있다. 파생 가능한 트레이트에는 Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord 및 Hash가 포함된다.

 같이 보기 
 확장 메서드
 인터페이스 (객체 지향 프로그래밍)
 매개변수 다형성
 UFCS

 각주 

 외부 링크 


분류:유형 이론
분류:C++
분류:프로그래밍 언어 주제