Cecha (programowanie komputerowe) - Trait (computer programming)

W programowaniu komputerowym , A cecha to pojęcie używane w programowaniu obiektowym , który stanowi zbiór metod , które mogą być wykorzystywane do rozszerzenia funkcjonalności klasy .

Charakterystyka

Cechy zapewniają zarówno zestaw metod, które implementują zachowanie w klasie, jak i wymagają, aby klasa implementowała zestaw metod, które parametryzują podane zachowanie.

W przypadku komunikacji międzyobiektowej cechy są gdzieś pomiędzy protokołem zorientowanym obiektowo (interfejsem) a domieszką . Interfejs może definiować jedno lub więcej zachowań za pomocą sygnatur metod , podczas gdy cecha definiuje zachowania za pomocą pełnych definicji metod, tj. zawiera treść metod . W przeciwieństwie do tego, domieszki zawierają pełne definicje metod i mogą również przenosić stan przez zmienną składową, podczas gdy cechy zwykle nie.

Stąd obiekt zdefiniowany jako cecha tworzony jest jako kompozycja metod, która może być wykorzystywana przez inne klasy bez konieczności wielokrotnego dziedziczenia . W przypadku kolizji nazewnictwa , gdy więcej niż jedna cecha, która ma być użyta przez klasę, ma metodę o tej samej nazwie, programista musi jednoznacznie określić, która z tych metod będzie używana w klasie; w ten sposób ręcznie rozwiązując problem diamentów wielokrotnego dziedziczenia. Różni się to od innych metod kompozycji w programowaniu obiektowym, w których sprzeczne nazwy są automatycznie rozwiązywane przez reguły określania zakresu .

Podczas gdy mixiny można komponować tylko za pomocą operacji dziedziczenia , cechy oferują znacznie szerszy wybór operacji, w tym:

  • suma symetryczna : operacja, która łączy dwie rozłączne cechy w celu stworzenia nowej cechy
  • override (lub suma asymetryczna ): operacja, która tworzy nową cechę poprzez dodanie metod do istniejącej cechy, ewentualnie zastępując niektóre z jej metod
  • alias : operacja, która tworzy nową cechę poprzez dodanie nowej nazwy do istniejącej metody
  • wykluczenie : operacja, która tworzy nową cechę poprzez usunięcie metody z istniejącej cechy. (Połączenie tego z operacją aliasu daje płytką operację zmiany nazwy ).

Cechy są komponowane w następujący sposób:

  • Kompozycja cech jest przemienna; kolejność dodawania cech nie ma znaczenia. Na przykład dana cecha S = A + B , to cecha T = B + A jest taka sama jak S .
  • Ze składu wyłączone są sprzeczne metody.
  • Cechy zagnieżdżone są równoważne cechom spłaszczonym; hierarchia kompozycji nie wpływa na zachowanie cech. Na przykład dana cecha S = A + X , gdzie X = B + C , wtedy cecha T = A + B + C jest taka sama jak S .

Obsługiwane języki

Cechy pochodzą pierwotnie z języka programowania Self i są obsługiwane przez następujące języki programowania:

  • AmbientTalk : Łączy właściwości cech Self (wielokrotne dziedziczenie obiektowe) i cech Smalltalk 's Squeak (wymagając wyraźnej kompozycji cech przez programistę). Opiera się na badaniach na stanowych i freezable cechami, aby umożliwić stan ciągu cech, których nie wolno było w pierwszych definicji.
  • C# : od wersji 8.0 C# obsługuje domyślne metody interfejsu , które mają pewne właściwości cech.
  • C++ : Używany w standardowej bibliotece szablonów i standardowej bibliotece C++ do obsługi ogólnych klas kontenerów oraz w bibliotece Boost TypeTraits.
  • Curl : Klasy abstrakcyjne jako domieszki umożliwiają implementację metod, a zatem stanowią cechy o innej nazwie.
  • D : Od wersji 2.003 rozszerzenie języka __traits i szablony pomocnicze modułu std.traits zapewniają cechy czasu kompilacji. Wraz z innymi funkcjami językowymi (zwłaszcza szablonami i domieszkami) umożliwiają elastyczne automatyczne generowanie metod w oparciu o interfejsy i typy. D umożliwia również jawne aliasowanie metod i zmiennych składowych, w tym przekazywanie do wielu klas składowych.
  • Twierdza
  • Groovy : Od wersji 2.3
  • Haskell : W Haskell cechy są znane jako klasy typu .
  • Haxe : Od wersji 2.4.0. Nazywany w instrukcji rozszerzeniem statycznym , używa usingsłowa kluczowego
  • Java : Od wersji 8 Java obsługuje metody domyślne , które mają pewne właściwości cech.
  • JavaScript : cechy mogą być implementowane poprzez funkcje i delegacje lub przez biblioteki, które dostarczają cechy.
  • Julia : Kilka pakietów implementuje cechy, np.
  • Kotlin : Cechy są nazywane interfejsami od M12.
  • Lasso
  • OCaml : Cechy mogą być implementowane przy użyciu różnych funkcji językowych: włączanie modułów i typów modułów, funktorów i typów funktorów, dziedziczenie klas i typów klas, i tak dalej.
  • Perl : Nazywane rolami , są zaimplementowane w bibliotekach Perla, takich jak Moose , Role::Tiny i Role::Basic. Role są częścią siostrzanego języka Raku .
  • PHP : Od wersji 5.4, PHP pozwala użytkownikom określać szablony, które zapewniają możliwość "dziedziczenia" z więcej niż jednej klasy (cechy), jako pseudo dziedziczenie wielokrotne .
  • Python : Za pośrednictwem biblioteki innej firmy lub za pośrednictwem klas mixin wyższego rzędu
  • Racket : Obsługuje cechy jako bibliotekę i używa makr, struktur i klas pierwszej klasy do ich implementacji.
  • Ruby : mixiny modułów mogą być użyte do implementacji cech.
  • Rdza
  • Cecha Scala jest wbudowana ze słowem kluczowym trait.
  • Smalltalk : Cechy są realizowane w dwóch dialektach Smalltalk, Squeak i Pharo .
  • Swift : Cechy mogą być implementowane z rozszerzeniami protokołu .

Przykłady

C#

W C# 8.0 możliwe jest zdefiniowanie implementacji jako elementu interfejsu.

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

PHP

W tym przykładzie użyto cechy, aby ulepszyć inne klasy:

// 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;
}

Pozwala to na symulację aspektów wielokrotnego dziedziczenia:

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)
    {
        // …
    }
}

Rdza

Cecha w Ruście deklaruje zestaw metod, które typ musi zaimplementować. Kompilatory Rusta wymagają wyjaśnienia cech, co zapewnia bezpieczeństwo generyków w Ruście.

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

Aby uprościć żmudną i powtarzającą się implementację cech takich jak Debugi Ord, derivemakro może być używane do żądania od kompilatorów automatycznego generowania niektórych implementacji. Cechy pochodne to: Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ordi Hash.

Zobacz też

Bibliografia

Zewnętrzne linki