Trait (電腦科學)

Trait物件導向程式設計中,是一個不可實例化(uninstantiable)的方法與類型的集合,為一個對象或演算法提供了策略(policy)或實現自身介面的細節功能。trait為類提供了一套方法,同時也意味著類必須實現這麼一套方法。

物件導向程式設計中,protocol、interface、trait、mixin具有類似的涵義。protocol/interface定義了函式原型;trait還定義了方法的完全實現;mixin除了方法的完整實現,還可以通過成員變數儲存了狀態資訊,而trait通常不會如此。理論上,traits支援一批組合(composition)操作:

  • 合併不相交的traits(symmetric sum)產生一個新的trait
  • 覆蓋(asymmetric sum):對一個已有的trait增加方法,可以覆蓋已有的方法
  • 別名(alias): 對已有方法增加別名,從而產生一個新的trait
  • 排除(exclusion):從已有trait中刪除方法從而產生一個新的trait
  • 巢狀的trait自動平面化。例如 給定trait S = A + X, 其中 X = B + C, 那麼trait T = A + B + C 等價於 S

C++編程中的traits

C++語言標準沒有定義"traits",但定義了"traits class":

17.3.25 [defns.traits] traits class

一個類,封裝了一套對類別模板和函式模板實例化時操縱對象類型是必須的類型和函式 [ 注釋: 定義在Clauses 21, 22 和 27的traits class是字元traits, 提供了string和iostream類所需的字元處理支援。]

C++標準模板庫中大量使用了traits。將因為模板形參(包括類型形參、非類型形參)不同而導致的不同抽取到新的模板(即traits)中去;然後通過traits的模板特化來實現針對具體情況的最佳化實現。Traits作為模板類,既聲明了統一的介面(包括類型、列舉、函式方法等),又可以通過模板特化,針對不同資料類型或其他模板參數,為類、函式或者通用演算法在因為使用的資料類型不同而導致處理邏輯不同時,提供了區分不同類型的具體細節,從而把這部分用Traits實現的功能與其它共同的功能區分開來。例如,容器的元素的不同資料類型,或者iostream是使用char還是wchar_t。一個traits包括了enum、typedef、模板偏特化(template partial specialization)。其中,enum定義了各種類的標識的統一表示;typedef定義了各個類的各自不同的類型定義,這對於使用模板元程式設計(template meta-programming)的靈活性非常重要;模板偏特化用於實現各個類的不同功能。

範例

假設有一個容器模板類,其包含元素的值類型可以是內生(built-in)的資料類型,也可以是自訂的類。因此,有的值類型支援move操作,有的值類型不支援move操作。該容器模板類具有統一的介面,但對不同的值類型實現了不同的語意功能。為此:

#include <iostream>

struct no_move{};//两个marker类型
struct has_move{};

struct myValueType{
  public: void move(){std::cout<<"move a myValueType obj."<<std::endl;}
};

template <typename T> struct traits{
  typedef no_move move_method; //对于traits模板类,默认为无move方法
  void move(T* p){}//默认为无move方法
};

template <> struct traits<myValueType>{
  typedef has_move move_method;//对于myValueType,traits模板偏特化,定义了有move方法
  void move(myValueType* p){p->move();} //模板特化,有move方法
};

template <typename T> struct Container{
  void move(T* p){traits<T>().move(p);};
};

int main()
{
  int i=101;
  int *p1=&i;
  myValueType v1;
  myValueType *pv=&v1;
  Container<int> c1;
  Container<myValueType> c2;
  c1.move(p1);
  c2.move(pv);
}

C#的支援

從C# 8.0開始,支援「預設介面方法」( default interface methods)。

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());
        }
    }

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

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

Haskell

Haskell中,traits稱作類型類(Type classes).

Java

從Java 8開始,支援預設方法(default method)

JavaScript

可以通過functions與delegations實現,或者通過支援traits的庫。

Python

通過第三方庫py3traits,或者高階mixin類。