Kto z was słyszał, że Java nie jest prawdziwym językiem OOP *dlatego*, że posiada prymitywy?

1
TomRiddle napisał(a):
koszalek-opalek napisał(a):
TomRiddle napisał(a):

PS: Ja może od siebie dodam że uważam że Java nie jest 100% językiem OOP, ale nie dlatego że posiada prymitywy.

A czemu? :)

[...] Głównym powodem, moim zdaniem czemu Java nie uchodzi za Pure OOP język jest brak wielodziedziczenia [...]

Nie lubię Javy bardzo -- ale akurat brak wielodziedziczenia jest w niej (moim zdaniem) załatwiony przez interfejsy -- w rozsądny sposób w każdej realnej sytuacji, gdzie byłoby ono potrzebne... Z wielodziedziczeniem są niestety kłopoty (zawsze jakieś) i ładnie ono tylko wygląda na papierze... W Pythonie i C++ też właściwie wielodziedziczenia nie używam, a jak już, to przez lekkie klasy abstrakcyjne (czyli interfejsy). W Pythonie zresztą tę sprawę całkiem załatwia kacze typowanie.

1
koszalek-opalek napisał(a):

Z wielodziedziczeniem są niestety kłopoty (zawsze jakieś) i ładnie ono tylko wygląda na papierze... W Pythonie i C++ też właściwie wielodziedziczenia nie używam, a jak już, to przez lekkie klasy abstrakcyjne (czyli interfejsy). W Pythonie zresztą tę sprawę całkiem załatwia kacze typowanie.

Możesz sobie racjonalizować brak wielodziedziczenia (klasowego) w Javie jak tylko chcesz; ale faktem jest że nie ma go w Javie, i moim zdaniem to jest jeden z powodow czemu Java nie jest 100% OOP.

0
TomRiddle napisał(a):
koszalek-opalek napisał(a):

Z wielodziedziczeniem są niestety kłopoty (zawsze jakieś) i ładnie ono tylko wygląda na papierze... W Pythonie i C++ też właściwie wielodziedziczenia nie używam, a jak już, to przez lekkie klasy abstrakcyjne (czyli interfejsy). W Pythonie zresztą tę sprawę całkiem załatwia kacze typowanie.

Możesz sobie racjonalizować brak wielodziedziczenia (klasowego) w Javie jak tylko chcesz; ale faktem jest że nie ma go w Javie, i moim zdaniem to jest jeden z powodow czemu Java nie jest 100% OOP.

Ja tam sobie nic nie racjonalizuje -- nie używam go po prostu także w nie-Javie :) -- napisałem tylko, dlaczego uważam, że nie ma potrzeby go używać. Jeśli używasz, że jest potrzebne, chętnie poznam Twoje zdanie.

0

Do czego wielodziedziczenie? Scala nie ma pełnego wielodziedziczenia, ale klasy mają tam możliwość rozszerzania wielu traitów (interfejsów z implementacją). Do czego to można użyć?
W kodzie produkcyjnym zykle do dodania loggera XD

class MyClass extends MyBaseClass with LazyLogging {
  logger.debug("This is very convenient ;-)")

  logger.whenDebugEnabled {
    println("This would only execute when the debug level is enabled.")
    (1 to 10).foreach(x => println("Scala logging is great!"))
  }
}

trait LazyLogging dodaje do naszej klasy loggera logger. Dzięki czemu we wszystkich klasach logger ma tą samą nazwę, a nie jak często w Javie jest na zmianę logger/log/LOGGER/LOG

W kodzie testowym traity są używane zamiast importów statycznych:

class IntSumSpec extends Specification with ScalaCheck { def is = s2"""

  The IntSum Monoid must respect Monoid laws
    left identity                                     $leftid
    right identity                                    $rightid
    associativity                                     $associativity
                                                      """
  import scalaz._, Scalaz._
  import IntSumMonoid._

  def leftid  = prop { i: Int => 0 |+| i === i }
  def rightid = prop { i: Int => i |+| 0 === i }

  def associativity = prop { (a: Int, b: Int, c: Int) =>
    ((a |+| b) |+| c) === (a |+| (b |+| c))
  }

}

Specification to klasa bazowa dla testów a ScalaCheck dodaje metody specyficzne dla property based testing (ktoś wie jak to jest po polsku?)

W Scali traity były używane jeszcze do wstrzykiwania zależności za pomocą Cake Pattern, ale to się chyba nie przyjęło.
Pewnie jeszcze pare zastosowań by się znalazło jak w ZIO, ale to już bardziej podpada pod FP niż OOP

0
jarekr000000 napisał(a):

Nie płakałbym nad tym, że da się w fp pisać kod prawie jak imperatywny:

  1. wymaga to pewnej ceremonii (i dobrze)

Prawdopodobnie dałoby się to znacznie skrócić używając notacji do z Haskella + szeregu dziwacznych operatorów

  1. te mutableRef jednak zwykle zachowują się lepiej przy dostępie z wielu wątków (tu oczywiście zależy od konkretnej biblioteki). (czyli w twoim przykładzie x = x+1 byłby atomowy w prawie każdej bibliotece do IO).

Zwykle tak, ale nikt nie broni mi upchać w efektach ubocznych (opakowanych w IO) dowolnie brzydkich rzeczy i tym samym mieć szereg bolączek znanych z języków imperatywnych.

Obstawiam, że dzięki monadzie IO dałoby się napisać transpiler np. z Javy do Haskella. Okazałoby się wtedy, że OOP jest podzbiorem FP :P

2

@Wibowit:

Obstawiam, że dzięki monadzie IO dałoby się napisać transpiler np. z Javy do Haskella. Okazałoby się wtedy, że OOP jest podzbiorem FP

Dokładnie.
W Eta-lang nawet wprowadzili monadę Java - poniekąd IO tyle tylko, że z dodatkowym obiektem (stan) reprezentującym this.
https://eta-lang.org/docs/user-guides/eta-user-guide/java-interop/java-interop-basics

Można było powiedzieć, że java to monada.

0

Głównym powodem, moim zdaniem czemu Java nie uchodzi za Pure OOP język jest brak wielodziedziczenia i to że nie można nadpisać Stringa, Integera, etc. i żeby pisać 100% należałoby zrobić swoje typy String, Integer które enkapsulują te Javowe, ale wtedy można standardową bibliotekę Javy wrzucić do kosza.

Nie wiem jak ma się brak wielodziedziczenia do OOP ale nie widzę sprzeczności obiektowości z klasami final. Dziedziczenie to najgorsza technika z OOP, kompozycja + interfejsy są lepsze w 90% przypadków.

0
Aleksander32 napisał(a):

Głównym powodem, moim zdaniem czemu Java nie uchodzi za Pure OOP język jest brak wielodziedziczenia i to że nie można nadpisać Stringa, Integera, etc. i żeby pisać 100% należałoby zrobić swoje typy String, Integer które enkapsulują te Javowe, ale wtedy można standardową bibliotekę Javy wrzucić do kosza.

Nie wiem jak ma się brak wielodziedziczenia do OOP ale nie widzę sprzeczności obiektowości z klasami final. Dziedziczenie to najgorsza technika z OOP, kompozycja + interfejsy są lepsze w 90% przypadków.

Nie mówisz o OOP teraz. Mówisz o pupularnym, komercyjno-korporacyjnym paradygmacie o nazwie: strukturalno-procedularno-funkcyjno-obiektowym-get-stuff-done, czyli tego czego najpewniej używasz w pracy. Nie masz zapewne w projekcie 100% functional, 100% procedural albo 100% OOP. Najprawdopodobniej masz ich mieszankę, taką żeby najniższym kosztem zbudować najlepszy produkt. Nie o tym jest ten temat.

Dziedziczenie to najgorsza technika z OOP, kompozycja + interfejsy są lepsze w 90% przypadków.

Jeśli masz aplikację w SpringBoot, masz MVC i podzieliłeś sobie to ładnie na serwisy i repozytoria, i całe dependency injection to jest @Autowire, to tak - faktycznie, dziedziczenie to najgorsza technika jaka istnieje. Ale to nie jest 100% OOP.

Ale kiedy popatrzysz na Big Picture, i zrozumiesz skąd wzięły się zasady OOP, co kierowało ich tworzeniem, jakie założenia zostały uznane, i jaka była intencja tego podejścia, to zauważysz wtedy że jest to niezbędny element żeby pisać kod OOP. To że używasz klas, polimorfizmu DI i Solid, to jeszcze nie znaczy że piszesz w OOP.

0

@TomRiddle: teraz próbujesz chyba jakos bardzo dogmatycznie do tego podejść

Nie o tym jest ten temat.

Piszę o tym że dziedziczenie jest najmniej OOP-ową techniką. Dziedziczenie chociażby łamie enkapsulacje

i całe dependency injection to jest @Autowired

Co ma autowired do DI? DI to DI, @Autowired to jedna z metod implementacji tego, co więcej właściwie najgorsza (i łamiąca trochę OOP bo bruździ zależność klas). A DI to jest akurat bardzo pro-OOP technika.

i jaka była intencja tego podejścia, to zauważysz wtedy że jest to niezbędny element żeby pisać kod OOP.

Nie wiem jaka była intencja, i nie wiem czy to jest niezbędny element OOP (to mnie akurat guzik obchodzi). Twierdzę że czasami dziedziczenie może mieć czasem sens w OOP, ale dziedziczenie generalnie łamie enkapsulacje i tworzy silne powiązania między klasami. Zreszta to nawet w tej słynnej książce o (obiektowych) wzorcach projektowych jest napisane żeby preferować kompozycje nad dziedziczenie.
Dodatkowo w czasach C++ na przykład nie było interfejsów więc dziedziczenie musiało być w pewnym miejscach stosowane, a ze smalltalka to średnio kojarze więc się nie wypowiem.

0

@Aleksander32:

Piszę o tym że dziedziczenie jest najmniej OOP-ową techniką. Dziedziczenie chociażby łamie enkapsulacje

Nawet, jeśli mam gettery? Klasa dziedzicząca nie jest klasą bazową, więc nie musi używać this, żeby odwołać się do pól z klasy bazowej - właściwie nawet nie powinna. Gdyby spojrzeć na klasę dziedziczącą jak na wzorzec w stylu dekorator, to ma sens.

3

Gettery i enkapsulacja? Pick one.

0

@Aleksander32: Zrób enkapsulację inaczej i bez rozdzielania kompilacji.

3

@PerlMonk: nie za bardzo rozumiem. Enkapsulacja polega na tym że po prostu nie udostępniasz stanu wewnętrznego obiektu, ani przez publiczne pola ani przez gettery/settery.

0
Aleksander32 napisał(a):

@TomRiddle: teraz próbujesz chyba jakos bardzo dogmatycznie do tego podejść

Nie o tym jest ten temat.

Piszę o tym że dziedziczenie jest najmniej OOP-ową techniką. Dziedziczenie chociażby łamie enkapsulacje

ta duskusja to jest ogólna czy tylko o javie bo nie wiem, ale java chyba też ma private i protected?

0

Oczywiście że ma, tylko jak robisz protected to udostępniasz bebechy innym klasom. Może mieć to sens, ale fakt jest taki że to tworzy silne zależności między klasami.

0

@Aleksander32: Jeśli nie można zmienić stanu obiektu, to równie dobrze klasa może być final i każde wywołanie metody stworzy nową instancję klasy. Idąc tym tokiem myślenia możemy programować strukturalnie. No bo co za różnica czy zrobię w C++

class Point {
	int x, y;
//gettery i settery
};

void func(Point a) {
	a.setX(1);
}

czy

struct Point {
	int x, y;
};

void setX(Point *a, int x) {
	a-> x = x;
}

void func(Point *a) {
	setX(a, 1);
}

? Ahh, zaraz... wtedy to nie będzie obiektowe.

2

@TomRiddle:

No w OOP wszystkie obiekty są immutable, i zawsze "mutacje" tworzą nowe instancje.

Czy ten wątek to nie jest jakiś wyrafinowany trolling...? :)

0
koszalek-opalek napisał(a):

@TomRiddle:

No w OOP wszystkie obiekty są immutable, i zawsze "mutacje" tworzą nowe instancje.

Czy ten wątek to nie jest jakiś wyrafinowany trolling...? :)

No właśnie odpowiedź nasuwa się od samego początku, ale sprowokować dyskusję trzeba.

0
koszalek-opalek napisał(a):

@TomRiddle:

No w OOP wszystkie obiekty są immutable, i zawsze "mutacje" tworzą nowe instancje.

Czy ten wątek to nie jest jakiś wyrafinowany trolling...? :)

PerlMonk napisał(a):

No właśnie odpowiedź nasuwa się od samego początku, ale sprowokować dyskusję trzeba.

No przecież w pure OOP wszystkie obiekty są immutable ;| I nie możesz edytować stanu, najwyżej możesz stworzyć nową inną instancję.

To co mi się nasuwa od początku to to, że nie mówicie o 100% OOP, tylko o jakiejś waszej wizji OOP.

1

No przecież w pure OOP wszystkie obiekty są immutable ;|

WUT? Nie pomyliło Ci się z FP? Przecież to w FP jest niemutowalność, lol xD

@PerlMonk

Jeśli nie można zmienić stanu obiektu, to równie dobrze klasa może być final i każde wywołanie metody stworzy nową instancję klasy

Ale co ma enkapsulacja do zmiany stanu? Czy jak masz LinkedList to masz samą strukturę "węzłów" schowaną przed światem zewnętrznym? No masz, dostajesz się tylko przez metody z API Listy. I tak działa enkapsulacja. Chociaż akurat ja jestem zwolennikiem niemutowalnych obiektów :P

0
Aleksander32 napisał(a):

No przecież w pure OOP wszystkie obiekty są immutable ;|

WUT? Nie pomyliło Ci się z FP? Przecież to w FP jest niemutowalność, lol xD

Widzę, że prowadzę z rozmowę z ludźmi którzy nie wiedzą o czym mówią (a przynajmniej co to jest OOP), tylko okopali się w swojej wizji. Dziękuję bardzo za dyskusję.

7

@TomRiddle:
Zanim zaczniemy pogrążać się w oparach absurdu i zaczniesz dalej zarzucać, że ludzie nie wiedzą o czym mówią to zapodaj może linkiem z wiedzą czym jest dla Ciebie OOP.
Na podstawie Twojej rozmowy z @Aleksander32 widzę, że jednak rozjechały Ci się koncepcje tożsamości i stanu. Bo w OOP jakie znam stan obiektu zmienia się bez zmiany tożsamości. Normalka, w praktycznie każdej instancji OOP - począwszy od Alana Kaya i Smalltalka.

3

W OOP tożsamość to, owszem, kluczowy aspekt -- ale właśnie dlatego, że stan się może zmieniać. :) W FP masz niemutowalność, więc tożsamość mało Cię obchodzi. I tak jest w istocie w implementacjach języków funkcyjnych, gdzie masz często różne (co do wartości, bo tylko to jest istotne) struktury składające się kawałkami z tych samych (w sensie tożsamości elementów w pamięci) fragmentów...

6

Mylące może być słowo. W pojęciu tożsamość (w kontekście programowania) chodzi o ten sam, a nie taki sam. Dla sprawdzenia czy mamy do czynienia z tym samym obiektem, porównujemy referencje (czyli to działa tylko dla typów referencyjnych). Dla sprawdzenia czy mamy do czynienia z takim samym obiektem, porównujemy zawartość.

3

Jeszcze taka fajna rzecz -- sigma-rachunek (teoretyczny/matematyczny model programowania obiektowego), opisany tutaj bardzo podstawowo...

Kluczowymi pojęciami w nim jest jaźń (czyli tożsamość w sensie jaki opisał @Wibowit) oraz nadpisanie metody (czyli zmiana zawartości obiektu bez zmiany jaźni).

0
Wibowit napisał(a):

Mylące może być słowo. W pojęciu tożsamość (w kontekście programowania) chodzi o ten sam, a nie taki sam. Dla sprawdzenia czy mamy do czynienia z tym samym obiektem, porównujemy referencje (czyli to działa tylko dla typów referencyjnych). Dla sprawdzenia czy mamy do czynienia z takim samym obiektem, porównujemy zawartość.

Nie bardzo rozumiem związku tożsamości z oop. Jeżeli masz listę i dodałeś coś na początek czyli masz my_list = [h | my_list] to jest to nowa lista pod względem referencji, ale to ta sama lista pod wzgledem tożsamości. W jaki sposób przeczy to oop?

0
twoj_stary_pijany napisał(a):

Nie bardzo rozumiem związku tożsamości z oop. Jeżeli masz listę i dodałeś coś na początek czyli masz my_list = [h | my_list] to jest to nowa lista pod względem referencji, ale to ta sama lista pod wzgledem tożsamości. W jaki sposób przeczy to oop?

To co napisałeś, to nie jest dodanie czegoś na początek listy, tylko utworzenie nowej, krórej ogonem jest stara. I to byłby taki bardziej funkcyjny przykład, bo u Ciebie my_list nie mutuje jako obiekt, tylko zmienia się to na co zmienna wskazuje. Dodanie czegoś byłoby czymś jak my_list.add_first(h) i byłoby mutujące, ale zachowujące tożsamość -- takie bardziej obiektowe. :)

0
koszalek-opalek napisał(a):
twoj_stary_pijany napisał(a):

Nie bardzo rozumiem związku tożsamości z oop. Jeżeli masz listę i dodałeś coś na początek czyli masz my_list = [h | my_list] to jest to nowa lista pod względem referencji, ale to ta sama lista pod wzgledem tożsamości. W jaki sposób przeczy to oop?

To co napisałeś, to nie jest dodanie czegoś na początek listy, tylko utworzenie nowej, krórej ogonem jest stara. I to byłby taki bardziej funkcyjny przykład, bo u Ciebie my_list nie mutuje jako obiekt, tylko zmienia się to na co zmienna wskazuje. Dodanie czegoś byłoby czymś jak my_list.add_first(h) i byłoby mutujące, ale zachowujące tożsamość -- takie bardziej obiektowe. :)

O -- takie coś można w Pythonie łatwo sprawdzić:

my_list_1 = [1, 2, 3]
my_list_2 = [1, 2, 3]

print(my_list_1)
print(my_list_2)
print(id(my_list_1))
print(id(my_list_2))

my_list_1 = [0] + my_list_1
my_list_2.insert(0, 0)

print(my_list_1)
print(my_list_2)
print(id(my_list_1))
print(id(my_list_2))

Wynik jest taki:

[1, 2, 3]
[1, 2, 3]
139822458658952
139822459170376
[0, 1, 2, 3]
[0, 1, 2, 3]
139822459170760
139822459170376

Zwróć uwagę jak zachowują się id -- wpierwszym (bardziej funkcyjnym) się zmienia, w drugim (bardziej obiektowym) nie.

0

@koszalek-opalek: ok, tylko jaki to ma związek z OOP?
Jeżeli mam statek i przez 10 lat na morzu wymieniłem w nim wszystkie deski to teraz mam inny statek czy to jest ten sam statek? W którym momencie nastąpiła podmiana?

1
twoj_stary_pijany napisał(a):

@koszalek-opalek: ok, tylko jaki to ma związek z OOP?

Jeżeli mam statek i przez 10 lat na morzu wymieniłem w nim wszystkie deski to teraz mam inny statek czy to jest ten sam statek? W którym momencie nastąpiła podmiana?

E, paradoks statku Tezeusza nie ma tu zastosowania. Tożsamość w programowaniu jest zwykle dobrze określona, a w przypadku Twojego przykładu to nie jest mutacja tylko konstrukcja nowego obiektu -- i już. Na to tylko wskazywałem.

1 użytkowników online, w tym zalogowanych: 0, gości: 1