Trait - po co to?

S9
  • Rejestracja:ponad 10 lat
  • Ostatnio:5 miesięcy
  • Lokalizacja:Warszawa
  • Postów:3573
1

Trait jest czymś pomiędzy interface a class -> można w nim implementować metody, można je zostawiać abstrakcyjne. Można również również dodać pola, choć kostruktorów już nie można utworzyć.
No i tak właściwie - na pierwszy rzut oka wydaje się praktycznie tym samym co klasa abstrakcyjna, z dwoma jedynie rożnicami - klasa abstrakcyjna może konstruktor, no i można dziedziczyć tylko po jednej klasie abstrakcyjnej.
Tak właściwie to czy ma to jakies dużo większe zastosowana? Czy daje to jakąś przewage nad stosowaniem klas abstrakcyjnych? Ja ogólnie nie za bardzo lubie dziedziczenia więc jakoś niezbyt entuzjastycznie podchodze to tego typu konstrukcji :D
@Wibowit @jarekr000000 wzywam :)


"w haśle <młody dynamiczny zespół> nie chodzi o to ile masz lat tylko jak często zmienia się skład"
edytowany 1x, ostatnio: scibi92
axelbest
  • Rejestracja:ponad 17 lat
  • Ostatnio:około godziny
  • Lokalizacja:Warszawa
  • Postów:2251
2

W sumie może odpowiem na bazie tego jak traity działają w phpie, no więc ...

  • klasa nie musi dziedziczyć po klasie abstrakcyjnej
  • klasa nie musi implementować konkretnego interfejsu
  • klasa może mieć wiele traitów

patrząc na powyższe - w przypadku niektórych zmian w kodzie, nie trzeba dowalać interfejsu czy klasy abstrakcyjnej. W moim odczuciu jest to uproszczenie, które pozwala uniknąć duplikacji kodu, jednocześnie nie narzucając powyższych wymagań - co przy istniejących klasach może narobić bigosu. No i skoro nie lubisz dziedziczenia, to trait może być właśnie dla Ciebie :) No ale wiesz... mówię o traitach w PHP... a to jest magia :P ( @jarekr000000 jako nasz naczelny czarodziej może jednak powie coś mądrzejszego)

edytowany 1x, ostatnio: axelbest
S9
  • Rejestracja:ponad 10 lat
  • Ostatnio:5 miesięcy
  • Lokalizacja:Warszawa
  • Postów:3573
0

@axelbest: nie wiem czy trairty w PHP działają tak samo, ale w sumie czym się de facto różni dziedzicenie czy tam implementacja traitu od dziedziczenia po klasie (poza tym że można dziedziczyć tylko po jednej klasie). Generalnie dziedziczenie jest dosyć silnie wiążace i ograniczania, więc staram się unikać dziedziczenia i wole kompozycje + interfejsy


"w haśle <młody dynamiczny zespół> nie chodzi o to ile masz lat tylko jak często zmienia się skład"
mr_jaro
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 3 lata
  • Lokalizacja:Grudziądz/Bydgoszcz
  • Postów:5300
0

trait to nie klasa, nie ma żadnego obiektu, to jest w zasadzie np jakaś metoda która w zupełnie róznych klasach, nic ze soba niepowiązanych wygląda tak samo i żeby nie pisać w każdej klasie tej metody robi się traita i wrzuca do takiej klasy


It's All About the Game.
edytowany 1x, ostatnio: mr_jaro
S9
  • Rejestracja:ponad 10 lat
  • Ostatnio:5 miesięcy
  • Lokalizacja:Warszawa
  • Postów:3573
0

No tak jak klasa abstrakcjna. może mieć również pola. Co to za różnica w takim razie bo ja nie widze :D


"w haśle <młody dynamiczny zespół> nie chodzi o to ile masz lat tylko jak często zmienia się skład"
mr_jaro
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 3 lata
  • Lokalizacja:Grudziądz/Bydgoszcz
  • Postów:5300
0

klasa abstrakcyjna wymaga posiadania metody abstrakcyjnej która trzeba zaimplementować no i trzeba po tej klasie dziedziczyć, ani jednego ani drugiego nie wymaga trait


It's All About the Game.
Pipes
  • Rejestracja:około 11 lat
  • Ostatnio:ponad 3 lata
  • Postów:459
0

Sam z traitów korzystam wtedy gdy chcę dodać jakieś funkcje do moich klas, a nie chcę aby one w jakiś sztuczny sposób dziedziczyły po jakiejś nadklasie albo duplikowały ten sam kod, gdyby miały implementować jakiś interejs. Mixiny w Rubim oferują to samo (tyle, że też dodają rzeczy do stdlib :) ).

Leroy
  • Rejestracja:prawie 10 lat
  • Ostatnio:ponad rok
  • Lokalizacja:Wrocław
  • Postów:107
0

Nie napisałeś o jaki język chodzi, założę że Scala.
Jest jeszcze wykorzystanie traitow do wzorca 'stackable modifications'.
Jak dla mnie taki decorator na sterydach.

A ogólnie to widziałem to zawsze podobnie; interfejsy z metodami i polami + bezpieczne pseudo wielodziedziczenie.

Zobacz pozostały 1 komentarz
Leroy
True, moja kulpa
YA
Temat mnie zaciekawił. Dekorator dorzuca funkcjonalność w runtime, traity wydawały mi się działać na etapie kompilacji. Teb stackable mods w scali to w runtime?
Leroy
Coz, chyba nie bardzo rozumiem pytanie ale spróbujmy. Tak jak przy dziedziczeniu nadpisujesz metodę w klasie zmieniając jej zachowanie dla wszystkich obiektów w compile time, tak zmienia się zachowanie wszystkich obiektów będących instancjami typu, który wychodzi z miksowania traitow używając stackable mods. Wg tego, można powiedzieć że w compile time. Ale I tak myślę że na żadne nie odpowiem lepiej niż pierwszy lepszy artykuł z google albo najlepiej rozdział Martina Oderskiego z Programming in Scala. Po tym sam sobie pewnie odpowiesz lepiej niż ja :)
DQ
Nie, stackable modifications są "rozwiązywane" na poziomie kompilacji, a nie w runtimie. Ma to bezpośredni związek z tym, że traity podlegają linearyzacji i właśnie wtedy kompilator Scali definiuje zachowania
YA
Dzięki, już jasne :)
S9
  • Rejestracja:ponad 10 lat
  • Ostatnio:5 miesięcy
  • Lokalizacja:Warszawa
  • Postów:3573
0
Pipes napisał(a):

Sam z traitów korzystam wtedy gdy chcę dodać jakieś funkcje do moich klas, a nie chcę aby one w jakiś sztuczny sposób dziedziczyły po jakiejś nadklasie albo duplikowały ten sam kod, gdyby miały implementować jakiś interejs. Mixiny w Rubim oferują to samo (tyle, że też dodają rzeczy do stdlib :) ).

W Scalii czy Groovym można zrobić te metody z Traitow finalne więc częściowo masz racje, ale czy w takim przypadku lepiej nie zrobić kompozycji? Generalnie bardzo rzadko spotykam się z tym żeby dziedziczenie było lepsze :D


"w haśle <młody dynamiczny zespół> nie chodzi o to ile masz lat tylko jak często zmienia się skład"
edytowany 1x, ostatnio: scibi92
DQ
  • Rejestracja:prawie 10 lat
  • Ostatnio:5 miesięcy
  • Postów:141
1

W sumie to większość różnic sam już znalazłeś ;) Najważniejsze z nich to:

  • Trait nie posiada argumentów (chociaż to ma się zmienić w Dotty)
  • Możesz rozszerzyć tylko jedną klasę abstrakcyjną ale za to mix-in'ować wiele traitów
  • Traity się stackują oraz podlegają linearyzacji

Traity często się stosuje w przypadku:

  • Dodania do klasy konkretnego zachowania, np. JsonWriter, JsonReader lub JsonFormat.
  • Tworzenia ADT (chociaż tutaj można się posługiwać klasą abstrakcyjną i niektórzy uważają (Sam Halliday), że tak powinno się je robić)
  • Type Class'y
  • Phantom types (aczkolwiek to nie jest za często stosowane)
    I pewnie masa wielu innych zastosowań o których nie pomyślałem teraz.

Oprócz tego patrząc z punktu koncepcji to trait'y powinny być stosowane tam, gdzie definiujesz jedno, konkretne, zazwyczaj generyczne zachowanie które może być wykorzystywane w wielu miejscach.

LS
  • Rejestracja:ponad 8 lat
  • Ostatnio:ponad 6 lat
  • Postów:990
0

Traity wymyślili gdy Java zrezygnowała z wielodziedziczenia. Jak widać nazwy się c zmieniają ale duch dalej silny :D


--
Annuit Coeptis
n0name_l
Traity to trochę przed Javą wymyślili. ;-)
YA
Wiele się musi zmienić, żeby wszystko zostało po staremu ;-)
LS
Ac używać zaczęli?
S9
  • Rejestracja:ponad 10 lat
  • Ostatnio:5 miesięcy
  • Lokalizacja:Warszawa
  • Postów:3573
0

No ja właśnie tak traktuje traity, rozwiązanie problemu z wielodziedziczeniem :D Ale np. w Kotlinie traity zostały wywalone :)


"w haśle <młody dynamiczny zespół> nie chodzi o to ile masz lat tylko jak często zmienia się skład"
LS
Problem pozostaje - co robić w przypadku konfliktów? Mam sympatię do dynamicznego runtime'u Obj-C gdzie za pomocą kompozycji i message forwardingu można było łatwo i przyjemnie rozszerzać funkcjonalność obiektów bez nadmiernej ingerencji w ich strukturę. Traciłeś oczywiście wsparcie kompilatora jeśli chodzi o sprawdzenie typów - no ale nie ma nic za darmo :/
DQ
Jeśli chcesz rozszerzyć funkcjonalność bez zmiany struktury klasy to wystarczy napisać implicit class'e z określonymi funkcjonalnościami. W ten sposób w ogóle nie ingerujesz w strukturę, jesteś w stanie dodawać nowe zachowania, a jednocześnie możesz je kontrolować poprzez odpowiednie importy
S9
@DisQ: te implicit classy to jak functions extensions? https://kotlinlang.org/docs/reference/extensions.html Bo przeczytałem coś na temat tego w Scalii ale ona ma dla mnie ciężką składnie
Leroy
@scibi92: extension functions to tylko jedne z ich przykładów wykorzystania. Sama ich idea jest dosyć prosta, ale możliwości do wykorzystania jest wiele
LS
@DisQ: Czy implicit classes pozwalają mi w razie konfliktu skorzystać z metod Traitu A a w przypadku konfliktu na metodzie z jej implementacji w Traicie B? Jak nie to nie mówimy o tym samym. Co do rozwiązywania konfliktów poprzez precedencję w deklaracji - dziękuję, postoję ;)
YA
  • Rejestracja:prawie 10 lat
  • Ostatnio:około godziny
  • Postów:2367
0

Odgrzebałem jeden stary artykuł, w którym traity są omawiane jako sposób realizacji pewnego pomysłu. https://www.artima.com/articles/dci_vision.html

Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 11 godzin
3

Interfejsy w Javie 8 pozwalają na wielodziedziczenie zachowań. Traity ze Scali pozwalały na to od dawna (nawet gdy Scala chodziła na Javie 5), ale dodatkowo pozwalają na dziedziczenie stanu - to akurat dość rzadko widuję, no chyba że w testach (ludzie mają tendencję na dopchanie do pojedynczego speca (klasy z zestawem testów) maksimum funkcjonalności, nawet nieużywanych lub źle zaprojektowanych).

Linearyzacja traitów (i co za tym idzie stackable traits) to pewien postęp w stosunku do wywalania się w czasie kompilacji na diamond problem. Typeclassy nie rozwiązują problemu, bo co jeśli dwie różne typeclassy zawierające identyczną metodę zostaną zaimplementowane dla jednego typu? Powracamy do diamond problem tylko w innej postaci. W Scali podpada to pod ambiguous implicits które są błędem kompilacji. Przykład: https://www.ideone.com/XdkJQK

Osobiście preferuję klasy abstrakcyjne nad traity z tego powodu, że klasy abstrakcyjne w Scali mają lżejsze kodowanie do bajtkodu niż traity. W końcu Scalowe klasy abstrakcyjne są praktycznie Javową konstrukcją, więc narzutu na nie nie ma. Natomiast cała linearyzacja traitów, wielodziedziczenie stanu, etc wymagają tworzenia dodatkowych syntetycznych metod co zarówno wydłuża czas kompilacji jak i zwiększa rozmiar wynikowego bajtkodu. Być może różnica nie jest wielka, ale zarazem wpisanie "abstract class" zamiast "trait" to też nie jest wielka różnica.

Najczęściej spotykane sensowne wykorzystanie wielodziedziczenia zachowania to sama hierarchia kolekcji Scalowych. Innym przykładem jest (tworzona przez użytkownika biblioteki ScalaTest) klasa bazowa dla testów: http://www.scalatest.org/user_guide/defining_base_classes - dodatkowo jest to przykład na wielodziedziczenie stanu, bo traity ze ScalaTesta często posiadają własny stan. W ogólności wielodziedziczenie implementacji, a już na pewno stanu należy stosować z rozwagą. Z drugiej strony implementowanie wielu traitów bez stanu i implementacji jest dość bezpieczne, aczkolwiek nie zawsze jest sens implementowania multum traitów przez jedną klasę.

@DisQ: Czy implicit classes pozwalają mi w razie konfliktu skorzystać z metod Traitu A a w przypadku konfliktu na metodzie z jej implementacji w Traicie B? Jak nie to nie mówimy o tym samym. Co do rozwiązywania konfliktów poprzez precedencję w deklaracji - dziękuję, postoję ;) - loza_szydercow 34 minuty temu

Kolejność deklaracji nie ma znaczenia, ale głębokość dziedziczenia owszem. W Scali często traity nazywa się LowPriorityImplicits i jest to pewien wzorzec pozwalający w pewnych przypadkach (a więc częściowo) rozwiązać diamond problem. Przykład: https://stackoverflow.com/a/33544705

Rozwiązywanie konfliktów implicitów przez kolejność implicitów w zasięgu też da się zrobić wykorzystując mechanizm przesłaniania. Wystarczy, że oba implicity mają tę samą nazwę - kompilator zobaczy tylko tą która jest zaimportowana jako ostatnia, a więc w kodzie:

Kopiuj
import konwersjeA.konwersjaImplicit
import konwersjeB.konwersjaImplicit
// kod wykorzystujące konwersjaImplicit

wykorzystana zostanie konwersja implicit pochodząca z konwersjeB.

Mechanizm przesłaniania wymaga importowania składowych do zasięgu, nie można przesłonić zmiennej deklarując ją dwukrotnie w tej samej przestrzeni nazw, np:

Kopiuj
object CośTam {
  val x: Int = 5
  val x: String = "ala ma kota" // błąd - tak nie zrobimy przesłaniania nazw
}

"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 8x, ostatnio: Wibowit
S9
  • Rejestracja:ponad 10 lat
  • Ostatnio:5 miesięcy
  • Lokalizacja:Warszawa
  • Postów:3573
0

@Wibowit: czyli w skrócie jeśli dobrze rozumiem to głownie jest po to żeby ominąc problem możliwości dziedziczenia tylko po jednej klasie? :D


"w haśle <młody dynamiczny zespół> nie chodzi o to ile masz lat tylko jak często zmienia się skład"
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 11 godzin
1

Generalnie tak, ale diabeł tkwi w szczegółach :]


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)