Odpowiedzi:
Przeciążenie metod można osiągnąć, deklarując dwie metody o tej samej nazwie i różnych podpisach. Te różne podpisy mogą być:
method(int a, int b) vs method(String a, String b)
method(a) vs method(a, b)
Nie możemy osiągnąć przeciążenia metod przy użyciu pierwszego sposobu, ponieważ nie ma deklaracji typu danych w ruby ( dynamiczny język typizowany ). Zatem jedynym sposobem zdefiniowania powyższej metody jestdef(a,b)
W przypadku drugiej opcji może się wydawać, że możemy osiągnąć przeciążenie metody, ale nie możemy. Powiedzmy, że mam dwie metody z różną liczbą argumentów,
def method(a); end;
def method(a, b = true); end; # second argument has a default value
method(10)
# Now the method call can match the first one as well as the second one,
# so here is the problem.
Tak więc ruby musi zachować jedną metodę w łańcuchu wyszukiwania metod o unikalnej nazwie.
„Przeciążanie” to termin, który po prostu nie ma sensu w Rubim. To jest w zasadzie synonimem „statycznego wysłania argumentu opartego na”, ale Ruby nie posiada statyczną wysyłkę w ogóle . Zatem powodem, dla którego Ruby nie obsługuje wysyłania statycznego na podstawie argumentów, jest to, że nie obsługuje on wysyłania statycznego, kropka. Nie obsługuje wysyłania statycznego żadnego rodzaju , opartego na argumentach lub w inny sposób.
Teraz, jeśli tak naprawdę nie pytasz konkretnie o przeciążanie, ale może o dynamiczne wysyłanie oparte na argumentach, odpowiedź brzmi: ponieważ Matz tego nie zaimplementował. Ponieważ nikt inny nie zadał sobie trudu, aby to zaproponować. Ponieważ nikt inny nie zadał sobie trudu, aby go wdrożyć.
Ogólnie rzecz biorąc, dynamiczne rozsyłanie oparte na argumentach w języku z opcjonalnymi argumentami i listami argumentów o zmiennej długości jest bardzo trudne do wykonania, a jeszcze trudniej jest zachować zrozumiałość. Nawet w językach ze statycznym wysyłaniem opartym na argumentach i bez argumentów opcjonalnych (jak na przykład Java), czasami dla zwykłego śmiertelnika prawie niemożliwe jest określenie, które przeciążenie zostanie wybrane.
W C # można faktycznie zakodować dowolny problem 3-SAT w celu rozwiązania przeciążenia, co oznacza, że rozwiązanie przeciążenia w C # jest NP-trudne.
Teraz spróbuj tego z dynamiczną wysyłką, gdzie masz dodatkowy wymiar czasu, który musisz zachować w swojej głowie.
Istnieją języki, które są wysyłane dynamicznie na podstawie wszystkich argumentów procedury, w przeciwieństwie do języków zorientowanych obiektowo, które wysyłają tylko na podstawie „ukrytego” self
argumentu zerowego . Na przykład Common Lisp rozsyła typy dynamiczne, a nawet wartości dynamiczne wszystkich argumentów. Clojure wysyła dowolną funkcję wszystkich argumentów (co przy okazji jest niezwykle fajne i niezwykle potężne).
Ale nie znam żadnego języka OO z dynamiczną dystrybucją opartą na argumentach. Martin Odersky powiedział, że mógłby rozważyć dodanie wysyłania opartego na argumentach do Scali, ale tylko wtedy, gdy może jednocześnie usunąć przeciążenie i być wstecznie kompatybilnym zarówno z istniejącym kodem Scala, który używa przeciążenia, jak i kompatybilnym z Javą (szczególnie wspomniał o Swing i AWT które odgrywają niezwykle złożone sztuczki, ćwicząc prawie każdy paskudny ciemny kąt raczej skomplikowanych reguł przeciążania Javy). Sam miałem kilka pomysłów na dodanie wysyłania opartego na argumentach do Rubiego, ale nigdy nie mogłem wymyślić, jak to zrobić w sposób zgodny wstecz.
def method(a, b = true)
nie zadziała, dlatego przeciążanie metod jest niemożliwe”. To nie jest; to jest po prostu trudne. Uważam jednak, że TA odpowiedź jest naprawdę pouczająca.
Zakładam, że szukasz możliwości zrobienia tego:
def my_method(arg1)
..
end
def my_method(arg1, arg2)
..
end
Ruby obsługuje to w inny sposób:
def my_method(*args)
if args.length == 1
#method 1
else
#method 2
end
end
Typowym wzorcem jest również przekazywanie opcji jako hash:
def my_method(options)
if options[:arg1] and options[:arg2]
#method 2
elsif options[:arg1]
#method 1
end
end
my_method arg1: 'hello', arg2: 'world'
Mam nadzieję, że to pomoże
Przeciążanie metod ma sens w języku z typowaniem statycznym, w którym można rozróżnić różne typy argumentów
f(1)
f('foo')
f(true)
a także między różnymi liczbami argumentów
f(1)
f(1, 'foo')
f(1, 'foo', true)
Pierwsze rozróżnienie nie istnieje w rubinie. Ruby używa dynamicznego pisania lub "kaczego pisania". Drugie rozróżnienie można obsłużyć domyślnymi argumentami lub pracując z argumentami:
def f(n, s = 'foo', flux_compensator = true)
...
end
def f(*args)
case args.size
when
...
when 2
...
when 3
...
end
end
To nie odpowiada na pytanie, dlaczego Ruby nie ma przeciążania metod, ale biblioteki innych firm mogą to zapewnić.
Contracts.ruby biblioteka umożliwia przeciążanie. Przykład zaczerpnięty z samouczka:
class Factorial
include Contracts
Contract 1 => 1
def fact(x)
x
end
Contract Num => Num
def fact(x)
x * fact(x - 1)
end
end
# try it out
Factorial.new.fact(5) # => 120
Zauważ, że jest to w rzeczywistości potężniejsze niż przeciążanie Javy, ponieważ możesz określić wartości do dopasowania (np. 1
), A nie tylko typy.
Zauważysz jednak zmniejszoną wydajność przy użyciu tego; będziesz musiał przeprowadzić testy porównawcze, aby zdecydować, ile możesz tolerować.
Często wykonuję następującą strukturę:
def method(param)
case param
when String
method_for_String(param)
when Type1
method_for_Type1(param)
...
else
#default implementation
end
end
Pozwala to użytkownikowi obiektu na użycie czystej i przejrzystej metody nazwa_metody: metoda Jeśli jednak chce zoptymalizować wykonanie, może bezpośrednio wywołać właściwą metodę.
Ponadto sprawia, że test jest bardziej przejrzysty i lepszy.
są już świetne odpowiedzi na pytanie dlaczego. Jeśli jednak ktoś szuka innych rozwiązań, sprawdź funkcjonalno-rubinową perełkę zainspirowaną dopasowaniem wzorów Elixir .
class Foo
include Functional::PatternMatching
## Constructor Over loading
defn(:initialize) { @name = 'baz' }
defn(:initialize, _) {|name| @name = name.to_s }
## Method Overloading
defn(:greet, :male) {
puts "Hello, sir!"
}
defn(:greet, :female) {
puts "Hello, ma'am!"
}
end
foo = Foo.new or Foo.new('Bar')
foo.greet(:male) => "Hello, sir!"
foo.greet(:female) => "Hello, ma'am!"