Wiem, że w rubinie nie ma pojęcia klasy abstrakcyjnej. Ale jeśli w ogóle trzeba to wdrożyć, jak się do tego zabrać? Próbowałem czegoś takiego ...
class A
def self.new
raise 'Doh! You are trying to write Java in Ruby!'
end
end
class B < A
...
...
end
Ale kiedy próbuję utworzyć wystąpienie B, wewnętrznie wywoła to wywołanie, A.new
które spowoduje zgłoszenie wyjątku.
Ponadto nie można tworzyć instancji modułów, ale nie można ich również dziedziczyć. ustawienie nowej metody jako prywatnej również nie zadziała. Jakieś wskazówki?
ruby
abstract-class
Chirantan
źródło
źródło
raise "Doh! You are trying to write Java in Ruby"
.Odpowiedzi:
Nie lubię używać klas abstrakcyjnych w Rubim (prawie zawsze jest lepszy sposób). Jeśli jednak naprawdę uważasz, że jest to najlepsza technika w tej sytuacji, możesz użyć następującego fragmentu kodu, aby bardziej zadeklarować, które metody są abstrakcyjne:
Zasadniczo wystarczy wywołać
abstract_methods
listę metod, które są abstrakcyjne, a gdy zostaną wywołane przez instancję klasy abstrakcyjnej,NotImplementedError
zostanie zgłoszony wyjątek.źródło
NotImplementedError
który zasadniczo oznacza „zależne od platformy, niedostępne na Twojej”. Zobacz dokumentację .Żeby zadzwonić późno, myślę, że nie ma powodu, aby powstrzymywać kogoś przed utworzeniem instancji klasy abstrakcyjnej, zwłaszcza że mogą dodawać do niej metody w locie .
Języki typu kaczego, takie jak Ruby, używają obecności / braku lub zachowania metod w czasie wykonywania, aby określić, czy powinny być wywoływane, czy nie. Dlatego twoje pytanie, w odniesieniu do metody abstrakcyjnej , ma sens
a to powinno dotyczyć końca historii. Jedynym powodem używania klas abstrakcyjnych w Javie jest naleganie, aby niektóre metody były „wypełniane”, podczas gdy inne zachowywały się w klasie abstrakcyjnej. W języku pisania kaczkami nacisk kładzie się na metody, a nie na klasy / typy, więc powinieneś przenieść swoje zmartwienia na ten poziom.
W swoim pytaniu zasadniczo próbujesz odtworzyć
abstract
słowo kluczowe z Javy, które jest zapachem kodu do robienia Javy w Rubim.źródło
Spróbuj tego:
źródło
#initialize
B, możesz po prostu podnieść cokolwiek w inicjalizacji A #if self.class == A
.źródło
dla każdego w świecie railsów implementacja modelu ActiveRecord jako klasy abstrakcyjnej jest wykonywana z następującą deklaracją w pliku modelu:
źródło
Moje 2 ¢: wybieram prostą, lekką mieszankę DSL:
I oczywiście dodanie kolejnego błędu przy inicjalizacji klasy bazowej byłoby w tym przypadku trywialne.
źródło
err = NotImplementedError.new(message); err.set_backtrace caller()
YMMVPrzez ostatnie półtora roku programowania w Rubim ani razu nie potrzebowałem klasy abstrakcyjnej.
Jeśli myślisz, że potrzebujesz klasy abstrakcyjnej, myślisz za dużo w języku, który ich dostarcza / wymaga, a nie w języku Ruby jako takim.
Jak sugerowali inni, mixin jest bardziej odpowiedni dla rzeczy, które mają być interfejsami (tak jak definiuje je Java), a przemyślenie projektu jest bardziej odpowiednie dla rzeczy, które „potrzebują” abstrakcyjnych klas z innych języków, takich jak C ++.
Aktualizacja 2019: Nie potrzebowałem klas abstrakcyjnych w Rubim od 16 i pół roku użytkowania. Wszystko, co mówią wszyscy ludzie komentujący moją odpowiedź, jest omówione przez faktyczną naukę języka Ruby i użycie odpowiednich narzędzi, takich jak moduły (które nawet zapewniają powszechne implementacje). W zespołach, którymi zarządzałem, są ludzie, którzy stworzyli klasy, których podstawowa implementacja kończy się niepowodzeniem (jak klasa abstrakcyjna), ale są to głównie marnowanie kodu, ponieważ
NoMethodError
dałyby dokładnie taki sam wynik jakAbstractClassError
w produkcji.źródło
Możesz wypróbować 3 rubygemy:
interfejs
streszczenie
proste streszczenie
źródło
Osobiście zgłaszam NotImplementedError w metodach klas abstrakcyjnych. Ale możesz zechcieć wykluczyć to z „nowej” metody z powodów, o których wspomniałeś.
źródło
initialize
metody rodzicielskie nie są wywoływane automatycznie, chyba że zostaną jawnie wywołane z super.Jeśli chcesz przejść z klasą niemożliwą do zainstalowania, w swojej metodzie A.new sprawdź, czy self == A przed wyrzuceniem błędu.
Ale tak naprawdę moduł wydaje się bardziej podobny do tego, co chcesz tutaj - na przykład Enumerable jest czymś, co może być klasą abstrakcyjną w innych językach. Technicznie nie można ich podklasy, ale dzwonienie
include SomeModule
pozwala osiągnąć mniej więcej ten sam cel. Czy jest jakiś powód, dla którego to nie zadziała?źródło
Czemu starasz się służyć klasie abstrakcyjnej? Prawdopodobnie jest lepszy sposób na zrobienie tego w rubinie, ale nie podałeś żadnych szczegółów.
Mój wskaźnik jest taki; użyj mieszanki, a nie dziedziczenia.
źródło
Inna odpowiedź:
Opiera się to na normalnym #method_missing do zgłaszania niezaimplementowanych metod, ale zapobiega implementacji klas abstrakcyjnych (nawet jeśli mają metodę inicjalizacji)
Tak jak powiedzieli inni plakaty, prawdopodobnie powinieneś używać mieszanki, a nie klasy abstrakcyjnej.
źródło
Zrobiłem to w ten sposób, więc na nowo definiuje nową klasę potomną, aby znaleźć nową klasę nieabstrakcyjną. Nadal nie widzę praktycznego zastosowania klas abstrakcyjnych w języku ruby.
źródło
Jest też ten mały
abstract_type
klejnot, który pozwala deklarować abstrakcyjne klasy i moduły w dyskretny sposób.Przykład (z pliku README.md ):
źródło
Nie ma nic złego w Twoim podejściu. Zgłoszenie błędu podczas inicjalizacji wydaje się być w porządku, o ile wszystkie podklasy oczywiście zastępują inicjalizację. Ale nie chcesz tak definiować siebie. Nowego. Oto, co bym zrobił.
Innym podejściem byłoby umieszczenie całej tej funkcjonalności w module, którego, jak wspomniałeś, nigdy nie można zainicjować. Następnie włącz moduł do swoich klas, zamiast dziedziczyć po innej klasie. Jednak to zepsułoby rzeczy takie jak super.
To zależy od tego, jak chcesz to zorganizować. Chociaż moduły wydają się czystszym rozwiązaniem problemu „Jak napisać coś, co jest przeznaczone do użycia przez inne klasy”
źródło
Klejnot 2-liniowy: https://rubygems.org/gems/abstract
źródło
Chociaż to nie wygląda jak Ruby, możesz to zrobić:
Wyniki:
źródło