Programowanie obiektowe - co o nim sądzicie?

2
piotrpo napisał(a):

@KamilAdam: Raczej ciężko stosować open/close bez dziedziczenia, interface separation bez interface'ów i LSP bez obiektów.

I tu pojawia się problem większości programistów OO. Jedyny runtime polimorfizm jaki znają to dziedziczenie (a konkretnie subtyping), a przecież są jeszcze traity w Ruscie i TypeClassy w Haskellu i multimetody w Clojure. I one też służą do open/close, interface separation i LSP. Więc mogę mieć SOLID kod w Ruście, mogę mieć SOLID kod w Haskellu i mogę mieć SOLID kod w Clojure

0

@Troll anty OOP: Zależy co piszesz. Jak sterownik drukarki, to pewnie OOP się nie nada. Do implementowania sieci neuronowych też się nie nadaje, ale co jest złego, żeby mieć coś takiego:

interface DataStorage<T> {
  fun query(criteria: Criteria): List<T>
  fun store(data: T)
}

I dowolną liczbę implementacji?

@KamilAdam: Całe to OOP ktoś tam wymyślił i była to ewolucja. Spięcie OOP w jakąś całość to SOLID, czyli zbór zasad, na temat których napisano doktoraty. Kiedy pani Liskov pisała o zastępowalności implementacji, to języki o których wspominasz nie istniały, a zasady zostały opisane dla OOP, tak jak ono wtedy wyglądało. Nie znaczy to, że części tych zasad nie można używać w językach funkcyjnych, albo chociaż stworzyć podobnych z przeznaczeniem dla języków funkcyjnych. Jednak dla mnie, miejsce z którego te zasady pochodzą, to OOP.

2
KamilAdam napisał(a):
piotrpo napisał(a):

@KamilAdam: Raczej ciężko stosować open/close bez dziedziczenia, interface separation bez interface'ów i LSP bez obiektów.

I tu pojawia się problem większości programistów OO. Jedyny runtime polimorfizm jaki znają to dziedziczenie (a konkretnie subtyping), a przecież są jeszcze traity w Ruscie i TypeClassy w Haskellu i multimetody w Clojure. I one też służą do open/close, interface separation i LSP. Więc mogę mieć SOLID kod w Ruście, mogę mieć SOLID kod w Haskellu i mogę mieć SOLID kod w Clojure

Ogólnie w wielu dyskusjach na temat OOP to wychodzi, że ludzie mówiąc o OOP, zwykle mają na myśli jego implementację w jakimś konkretnym języku (głównie chyba w C++, Javie, C#). Niezależnie czy ci ludzie to OOP chwalą, krytykują czy po prostu mówią, to nawet nie zauważają, że ich myślenie jest skażone implementacją czy zwyczajami z danego języka czy danej grupy programistów (np. jeśli dużo programistów robiąc OOP nadużywa dziedziczenia, to nie znaczy, że OOP jest złe, tylko że dziedziczenie jest nadużywane. Może też to znaczyć, że w jakimś języku trzeba użyć dziedziczenia, bo inaczej się nie skompiluje albo będzie trudno osiągnąć dany cel. Albo po prostu, że dziedziczenie jest idiomatyczne dla tego języka).

0
piotrpo napisał(a):

@KamilAdam: Całe to OOP ktoś tam wymyślił i była to ewolucja. Spięcie OOP w jakąś całość to SOLID, czyli zbór zasad, na temat których napisano doktoraty. Kiedy pani Liskov pisała o zastępowalności implementacji, to języki o których wspominasz nie istniały, a zasady zostały opisane dla OOP, tak jak ono wtedy wyglądało. Nie znaczy to, że części tych zasad nie można używać w językach funkcyjnych, albo chociaż stworzyć podobnych z przeznaczeniem dla języków funkcyjnych. Jednak dla mnie, miejsce z którego te zasady pochodzą, to OOP.

Powiem szczerze że nic nie zrozumiałem z tego wpisu. To:

  1. mogę mieć SOLID w Ruscie mimo że nie jest OO
  2. nie mogę mieć SOLID w Ruscie
  3. Rust jest OO

?

0

@KamilAdam: Nie znam Rusta. Natomiast w językach, które nie wspierają OOP, nie możesz mieć wszystkiego. Takie SRP oryginalnie oznacza, że klasa ma mieć pojedynczą odpowiedzialność / powód do zmiany. Możesz sobie to zmienić na "funkcja ma pojedynczą odpowiedzialność", nazwać to separacją odpowiedzialności, ale to już nie będzie ta oryginalna zasada. Nie, żeby było to złe, ale w takim C, możesz pisać zgodnie z duchem SOLID, ale nie będziesz pisać oryginalnego SOLID, bo te zasady nie mówią o programowaniu strukturalnym. Pytałeś o święte księgi OOP i moim zdaniem są to publikacje stojące za stworzeniem SOLID.

0
piotrpo napisał(a):

@KamilAdam: Nie znam Rusta. Natomiast w językach, które nie wspierają OOP, nie możesz mieć wszystkiego. Takie SRP oryginalnie oznacza, że klasa ma mieć pojedynczą odpowiedzialność / powód do zmiany.

Czyli w Ruscie nie można pisać SOLID bo Rust ma traity i implementacje traitów, ale jakby przemianować słowo kluczowe z impl na class to już by można było pisać SOLID?

Czyli ten kod nie jest SOLID:

pub trait Summary {
    fn summarize(&self) -> String;
}

pub struct NewsArticle {
    pub headline: String,
    pub location: String,
    pub author: String,
    pub content: String,
}

impl Summary for NewsArticle {
    fn summarize(&self) -> String {
        format!("{}, by {} ({})", self.headline, self.author, self.location)
    }
}

ale jakby istniał hipotetyczny język SolidRust używający słowa kluczowego class zamiast impl to ten kod byłby SOLID

pub trait Summary {
    fn summarize(&self) -> String;
}

pub struct NewsArticle {
    pub headline: String,
    pub location: String,
    pub author: String,
    pub content: String,
}

class Summary for NewsArticle {
    fn summarize(&self) -> String {
        format!("{}, by {} ({})", self.headline, self.author, self.location)
    }
}

to ciesze się że sobie to wyjaśniliśmy :D

Teraz już wiem że w Haskellu można pisać SOLID bo ma klasy:

-- interfejs czyli TypeClassa
class Functor f where
  fmap                  :: (a -> b) -> f a -> f b

-- implementacja czyli instancja TypeClassy
instance Functor Tree where
  fmap f (Leaf x)       = Leaf   (f x)
  fmap f (Branch t1 t2) = Branch (fmap f t1) (fmap f t2)

:P

0

@KamilAdam: Trochę nie wiem z czym dyskutujesz i jaka jest twoja teza. Skoro domagasz się mojego komentarza odnośnie tych snipletów w Rust, to:
Struct nie jest klasą. Tak, jeżeli w jakiejś tam Javie wstawisz słowo kluczowe class, to się skompiluje, ale nadal nie jest to klasą w znaczeniu obiektowości. Istotą (w moim przekonaniu i moim bardzo skromnym zdaniem) jest łączenie danych i zmiennych w jeden byt (obiekt), który samodzielnie kontroluje swój stan (dane), a na zewnątrz udostępnia jedynie metody. które ten stan zmieniają, albo nie - nikomu z zewnątrz nic do tego. Moim zdaniem, to właśnie ten element jest kwintesencją "obiektowości". To, że pojawia się dziedziczenie, polimorfizm, interface'y i ich implementacje, dziedziczenie, wzorce projektowe itd - fajnie, użyteczne (albo i nie) narzędzia, ale w moim przekonaniu są to jedynie dodatki pozwalające efektywniej stosować prostą konwencję z pudełkiem zawierającym prywatne dane i logikę i oferującym publiczne metody.
Dla odmiany, programowanie funkcyjne to dążenie do sytuacji, w której każda jednostka kodu przybiera postać y=f(x) a f nie przechowuje żadnego stanu, zewnętrznych odwołań poza wejściem i wyjściem.
Spróbuję jeszcze raz wyjaśnić, dlaczego uważam, że kod, który nie jest obiektowy, nie może być (znowu, moim zdaniem) SOLID. Akronim ten, na podstawie wcześniejszych prac wielu innych autorów ułożył Wujek Bob w swojej książce o OOD, w kontekście programowania obiektowego. Dlatego ciężko mówić o SOLID w przypadku języków, które nie są obiektowe. To trochę tak, jak byś do buddyzmu wrzucił katolickie bezmięsne piątki.

1
piotrpo napisał(a):

Istotą (w moim przekonaniu i moim bardzo skromnym zdaniem) jest łączenie danych i zmiennych w jeden byt (obiekt), który samodzielnie kontroluje swój stan (dane), a na zewnątrz udostępnia jedynie metody. które ten stan zmieniają, albo nie - nikomu z zewnątrz nic do tego.

To brzmi jak opis Rusta XD

To, o czym piszesz, Rust kontroluje na poziomie języka przez borrow-checker. To języki niby obiektowe często nie są w stanie zagwarantować, że obiekt trzyma i zmienia tylko swoje dane, ponieważ nie ma w nich koncepcji własności danych czy pilnowania, żeby dane nie były mutowane przez kogo popadnie i kiedy popadnie tylko dlatego, że jakiś kawałek kodu uzyskał referencję do kawałka danych i ją sobie zatrzymał, mimo że nie powinien.

2
piotrpo napisał(a):

Spróbuję jeszcze raz wyjaśnić, dlaczego uważam, że kod, który nie jest obiektowy, nie może być (znowu, moim zdaniem) SOLID. Akronim ten, na podstawie wcześniejszych prac wielu innych autorów ułożył Wujek Bob w swojej książce o OOD, w kontekście programowania obiektowego. Dlatego ciężko mówić o SOLID w przypadku języków, które nie są obiektowe

OK, czyli Wujek Bob wymyślił SOLID dla OOP i SOLID nie ma sensu w FP, racja?

No chyba że Wujek Bob powie inaczej w kolejnej ksiażce to wtedy SOLID ma sens w FP, racja?
https://www.oreilly.com/library/view/functional-design-principles/9780138176518/

Martin's approach is pragmatic, minimizing theory in favor of "in the-trenches" problem-solving. Through accessible examples, working developers will discover how the easy-to-learn, semantically rich Clojure language can help them improve code cleanliness, design, discipline, and outcomes. Martin examines well-known SOLID principles and Gang of Four Design Patterns from a functional perspective, revealing why patterns remain extremely valuable to functional programmers, and how to use them to achieve superior results.

PS Released October 2023 ciekawe ile będzie trzebać czekać na tłumaczenie :(

1
piotrpo napisał(a):

@Troll anty OOP: Zależy co piszesz. Jak sterownik drukarki, to pewnie OOP się nie nada. Do implementowania sieci neuronowych też się nie nadaje, ale co jest złego, żeby mieć coś takiego:

interface DataStorage<T> {
  fun query(criteria: Criteria): List<T>
  fun store(data: T)
}

I dowolną liczbę implementacji?

W większości przypadków mamy liczbę implementacji 1, w innych przypadkach jakąś małą i na tyle rzadko zmieniającą się liczbę tych implementacji, że nie trzeba przygotowywać programu pod działanie z dowolną liczbą. Często też nie ma żadnej potrzeby, aby implementacje były dynamicznie ładowane jako plugin, więc nic nie stoi na przeszkodzie, by zrealizować to zwykłą funkcją, operującą na discriminated union/tagged union.

Odbyła się niedawno ciekawa internetowa dyskusja między Robertem Martinem, autorem "Clean Code", a krytykującym ideę z tej książki Casey-em Muratori. I w niej jest ładnie omówiony przykład jednego interfejsu i skutków podejścia w stylu OOP i podejścia z discriminated union.

https://github.com/unclebob/cmuratori-discussion/blob/main/cleancodeqa-2.md

Dość długi tekst zaczynający się od "CASEY: I'm not even talking about machine-cycles, I was just focusing on programmer-cycles."

10

Kurde, Uncle Bob mi odpisał. Chyba wydrukuję i sobie w ramkę oprawię
Screenshot 2023-09-27 171148.png
Twit

2

@piotrpo:

Dla odmiany, programowanie funkcyjne to dążenie do sytuacji, w której każda jednostka kodu przybiera postać y=f(x) a f nie przechowuje żadnego stanu, zewnętrznych odwołań poza wejściem i wyjściem.

A co z taką funkcją?

f :: świat -> świat

Ona nie trzyma stanu? Czy procesy w Erlangu nie trzymają stanu?

Pojadę klasykiem:

OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.

Alan Kay

Brak tudzież obecność słówka kluczowego class jest kompletnie bez znaczenia w przypadku obiektowości.

0
twoj_stary_pijany napisał(a):

@piotrpo:
A co z taką funkcją?

f :: świat -> świat

W którym miejscu ten stan jest trzymany? Masz przyjęty parametr, zrobione coś, zwrócony wynik. Proce produkowania wyniku może oczywiście przechowywać swój chwilowy stan w postaci jakichś zmiennych pomocniczych, ale nie jest to stan obiektu. Zakładam, że świat jest tutaj immutable.

Pojadę klasykiem:

OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.

Alan Kay

No a dla mnie oznacza łączenie danych z logiką.

Brak tudzież obecność słówka kluczowego class jest kompletnie bez znaczenia w przypadku obiektowości.

Ale ja się z tym zgadzam. W żadnym momencie nie odnoszę się do jakiegokolwiek konkretnego języka programowania. W C da się pisać "obiektowo", chociaż będzie to mocno upierdliwe. Powstał nawet taki potworek jak ObjectiveC. Oczywiście są języki, w których OOP przychodzi nieco prościej, tak jak są języki, w których pisanie funkcyjnie jest prostsze.

2
piotrpo napisał(a):
twoj_stary_pijany napisał(a):

@piotrpo:
A co z taką funkcją?

f :: świat -> świat

W którym miejscu ten stan jest trzymany? Masz przyjęty parametr, zrobione coś, zwrócony wynik. Proce produkowania wyniku może oczywiście przechowywać swój chwilowy stan w postaci jakichś zmiennych pomocniczych, ale nie jest to stan obiektu. Zakładam, że świat jest tutaj immutable.

To jest klasyczny przypadek tłumaczący jak działa monada IO. To co robi IO (monada) to ukrywa ten parametr, żeby nie trzeba było się nim babrać. Ale równie dobrze mógłby być explicite.

Wyobraź sobie, że masz dostarczony przez twój stdlib zestaw funkcji:


fun print(Świat, String) : Świat

fun read(Świat) : (Świat, String)

fun executeSql(Świat, String) : (Świat, Result)

A twoja funkcja main ma mieć postać:

fun main(Świat): Świat 

Mając odpowiednio dużo funkcji bazowych (implementowanych przez platformę (czyli zwisa Ci jak)) - możesz pisać swój kod zupełnie czysto (funkcje będą z definicji pure- o ile tylko funkcje bazowe bedą miały takie własności), mimo że wykonują te wszystkie operacje (przekształcania świata). I tak, świat jest tu immutable.

EDIT:
Minimalnie się zapędziłem, bo zapomniałem, że Turyng to świnia. Powyższe przykłady mogą być czyste dla prostych programów.
Żeby było czysto w bardziej skomplikowanych przypadkach to funkcje biblioteczne powinny mieć postać w stylu jak poniżej (pokraczne niestety).

fun read(Świat, (String)->Świat) : Świat
0

@jarekr000000: Ale ja rozumiem czym jest funkcja pure. Rozumiem, że w trakcie wykonywania jej instancja (wątek?) może mieć wewnętrzny proces i ten proces może mieć jakiś stan. Tylko to nie będzie stan "obiektu". No i można ten problem załatwić przechowując odpowiednik stanu tego obiektu na zewnątrz funkcji. Nie wiem dlaczego od kilku postów różne osoby próbują mi tłumaczyć rzeczy oczywiste.

0

@twoj_stary_pijany: No to argumenty funkcji nie są wewnętrznym stanem obiektu? W ten sposób patrząc - oczywiście jest to obiekt i ma swój stan. Patrząc przez okulary OOD/OOP wszystko jest obiektem. Natomiast dla mnie jest istotna różnica pomiędzy:

class Counter{
  var count = 0
  fun increment(){
    count++
  }
}

a

fun increment(i: Int): Int = i++
0

Tak naprawdę można sobie wyobrazić obiektowy Counter, ale niemutowalny.

class Counter {
  private int count;

  public Counter (int c) {
    count = c;
  }

  public int getCount () {
    return count;
  }

  public Counter increment () {
    return new Counter (count + 1);
  }
}
0
Manna5 napisał(a):

Tak naprawdę można sobie wyobrazić obiektowy Counter, ale niemutowalny.

Jako przykład spoko, ale tak naprawdę przeinżynierowana wersja inta xD chociaż takie podejście może mieć sens, jeśli chcielibyśmy zrobić reaktywny prymityw/obserwable. Czyli powiedzmy, że za każdym razem, kiedy ktoś zmieni counter, to odpala się jakiś event. Takie podejście jest przydatne jak się robi np. jakiś framework frontendowy, który po zmianie wartości odświeża widok. Wtedy opakowanie w klasę zwykłej liczby czy innej banalnej wartości może mieć sens, bo można dodać później nasłuchiwanie eventów choćby.

Edit: chociaż zaraz. Wtedy musiałby być mutowalny (przynajmniej od zewnątrz) żeby zachować stałą referencję.

3
LukeJL napisał(a):
Manna5 napisał(a):

Tak naprawdę można sobie wyobrazić obiektowy Counter, ale niemutowalny.

Jako przykład spoko, ale tak naprawdę przeinżynierowana wersja inta xD

To nie jest żadna przeinżynierowana wersja inta, przede wszystkim dlatego, ze działa inaczej niż int. Np. możesz to tylko zwiększać.
Lepiej by to było widać gdyby to było postaci:

class Counter {
  final int count;

  public Counter() {
    count = 0;
  }

  private Counter (int c) {
    count = c;
  }

  public int getCount () {
    return count;
  }

  public Counter increment () {
    return new Counter (count + 1);
  }
}

Takich typów używa się gdy:

  1. chcemy mieć dodatkowe ograniczenia (w stosunku do typów prymitywnych):
    np.
class PESEL(p:String)
class ZipCode(c:String)
class Age(a: unsigned int)
  1. w celu lepszego dokumentowania kodu
  2. oraz (chyba najważniejsze) pozwala to na lepszą kontrole poprawności kodu (ściślejsze typy),
    np. unikniemy takiego głupiego błędu
  user.age = inputData.address.houseNr;

I co najlepsze, w nowoczesnych językach/kompilatorach takie opakowywanie kosztuje prawie nic, a czasem nawet dokładnie nic.

1
jarekr000000 napisał(a):
  1. oraz (chyba najważniejsze) pozwala to na lepszą kontrole poprawności kodu (ściślejsze typy),

To mi przypomina new type idiom z Rusta https://doc.rust-lang.org/rust-by-example/generics/new_types.html

1
LukeJL napisał(a):
jarekr000000 napisał(a):
  1. oraz (chyba najważniejsze) pozwala to na lepszą kontrole poprawności kodu (ściślejsze typy),

To mi przypomina new type idiom z Rusta https://doc.rust-lang.org/rust-by-example/generics/new_types.html

Newtype w Haskellu jest trochę starszy https://wiki.haskell.org/Newtype

BTW W Scali 2 trzeba było rzeźnić to ręcznie, ale w Scali 3 jest Opaque type https://docs.scala-lang.org/scala3/book/types-opaque-types.html (co za chory człowiek wymyślał tą nazwę to ja nie wime :D )

0
jarekr000000 napisał(a):
LukeJL napisał(a):
Manna5 napisał(a):

Tak naprawdę można sobie wyobrazić obiektowy Counter, ale niemutowalny.

Jako przykład spoko, ale tak naprawdę przeinżynierowana wersja inta xD

To nie jest żadna przeinżynierowana wersja inta, przede wszystkim dlatego, ze działa inaczej niż int. Np. możesz to tylko zwiększać.
Lepiej by to było widać gdyby to było postaci:

class Counter {
  final int count;

  public Counter() {
    count = 0;
  }

  private Counter (int c) {
    count = c;
  }

  public int getCount () {
    return count;
  }

  public Counter increment () {
    return new Counter (count + 1);
  }
}

Takich typów używa się gdy:

  1. chcemy mieć dodatkowe ograniczenia (w stosunku do typów prymitywnych):
    np.
class PESEL(p:String)
class ZipCode(c:String)
class Age(a: unsigned int)
  1. w celu lepszego dokumentowania kodu
  2. oraz (chyba najważniejsze) pozwala to na lepszą kontrole poprawności kodu (ściślejsze typy),
    np. unikniemy takiego głupiego błędu
  user.age = inputData.address.houseNr;

I co najlepsze, w nowoczesnych językach/kompilatorach takie opakowywanie kosztuje prawie nic, a czasem nawet dokładnie nic.

Ja nie dałem tego final ani bezargumentowego konstruktora tylko po to żeby skrócić przykład. I w twojej wersji albo zjadłeś private dla pola count, albo, jeżeli to nie ma być prywatne, to nie potrzebujesz gettera.

0

Moim zdaniem głównym problemem OOP jest to, że nie zakazuje programistom popełniania błędów.

A tak poza tym, to dużym problemem jest to, że ludzie boją się definiować swoje typy (mimo, że to w teorii podstawa OOP), więc kod wygląda jak wygląda, i działa jak działa.

5

OOP umożliwiło całym generacjom programjerów modelowanie złożonych fragmentów świata rzeczywistego w systemach komputerowych.

Jasne, OOP nie jest idealne oraz w $current_year jesteśmy bardziej świadomi jego ograniczeń, problemów i miejsc które generują problemy (np. dziedziczenie)

Jednakże nadal mimo to przez te kilka dekad powstawała tona różnorakiego softwareu, pisanego przez programistów z 0 oraz 30 lat expa i robiła to, co powinna.

Myślę że z tego względu można podsumować OOP jako "sukces".

@Riddle
@Krolik: @rjakubowski obaj jesteście trochę w błędzie - przecież OOP nigdy nie służyło do żadnego modelowania niczego :|
To jest właśnie jedna z miskoncepcji czym jest OOP. Taki pomysł jak "modelowanie świata" to jest jakaś bzdura.

Riddle już chyba odleciał.

Jeżeli OOP nie jest narzędziem do modelowania, to ja nie wiem :D

Brak zrozumienia czym jest paradygmat programowania.

1

Nie wiem jak obecnie wyglądają ścieżki rozwoju programisty ale kiedyś zaczynało się od programowania strukturalnego a potem z mniejszym lub większym bólem wchodziło się w OOP w Pascalu czy też w C++.W tej drodze np. takie TurboVision było dobrą lekcją i jak na tamte czasy świetnie pokazywało siłę tej jednak dość prostej idei jaką jest OOP. Dobre zrozumienie tego paradygmatu to narzędzie, które w rękach sprawnego programisty sprawdza się do dziś. Mimo, że stają się coraz bardziej popularne inne podejścia to pozostając tylko w OOP wciąż możemy zamodelować całe ekosystemy aplikacji i będziemy mieli nad tym dobrą kontrolę. Dla mnie obiektowa reprezentacja klocków systemu jest najbardziej naturalna i najłatwiejsza do zaprezentowania klientowi a to często skraca proces projektowania i przekłada się na szybsze wykonanie systemu.
Tak jak napisał @WeiXia, także odnoszę wrażenie, że nie wszyscy wypowiadający się na temat OOP rzetelnie odrobili zadanie domowe z tego przedmiotu.

Nawet strukturalnie da się wszystko napisać - choć osobiście bym nie chciał. Chciałbym natomiast porozmawiać z kimś kto modeluje systemy, zajmuje się analityką / rozmawia z klientem i programuje w paradygmacie funkcyjnym. Ciekaw jestem jak wyglądają notatki takiej osoby, co na spotkaniach pokazuje klientom itp. itd.

2
4w0rX4t4X napisał(a):

Chciałbym natomiast porozmawiać z kimś kto modeluje systemy, zajmuje się analityką / rozmawia z klientem i programuje w paradygmacie funkcyjnym. Ciekaw jestem jak wyglądają notatki takiej osoby, co na spotkaniach pokazuje klientom itp. itd.

A co konkretnie chciałbyś wiedzieć? Bo mój klient komunikuje się ze mną historyjką:

Jako użytkownik z prawami W
Chciałbym zaimportować plik X
żeby widzieć dane Y na stronie Z

I potem jest opis pliku X i mockupy strony Z z danymi Y

A to czy ja to napiszę sobie funkcynie czy imperatywnie to klienta mało obchodzi

BTW schemat mikroserwisów tez wygląda tak samo :D

1

@KamilAdam: bardzo to uprościłeś. A teraz wyobraź sobie, że masz opisać system, który pomaga zarządzać firmą, w której:

  • jest jedna firma matka,
  • istnieje sieć oddziałów, które tworzą strukturą hierarchiczną. Każdy oddział ma jeden lub więcej adresów. Każdy oddział specjalizuje się w 10 czynnościach.
  • do każdego oddziału są przypisani pracownicy, który stanowiska w ramach oddziałów także są ułożone hierarchiczne.
  • firma ma samochody - jednak tu wszystkie są przypisane do centrali i są "wypożyczane" oddziałom a koszty tych wynajmów są rozliczane w jednej wspólnej tabeli rozliczenia najmu samochodów (tak oddziały płacą centrali).
  • priorytet w kolejce do wypożyczenia auta ma ją najpierw oddziały stojące wyżej w strukturze a w ramach oddziałów starsi stanowiskiem.
  • chcemy mieć możliwość drukowania raportów: gdzie są wypożyczone auta w danej chwili ile czasu i kto wypożyczał.

Taki przykładowy kawałek rzeczywistości, z jakim można się spotkać podczas realizowania zleceń nic wybitnie skomplikowanego a jednocześnie nie kilka trywialnie powiązanych encji.

A jak już to wszystko się ułoży to klient powie, że w zależności od stanowiska:

  • manager może wypożyczać auto max na 50 godz./mies,
  • scrummaster na 5 godz,
  • a senior developer na 30 minut.
    Limity będą najpierw globalne dla całej firmy ale po tygodniu jednak okaże się, że każdy oddział będzie mógł je zmieniać wg własnych potrzeb bo zostanie wprowadzony nowy sumaryczny limit godzin na oddział.
0
4w0rX4t4X napisał(a):

@KamilAdam: bardzo to uprościłeś. A teraz wyobraź sobie, że masz opisać system, który pomaga zarządzać firmą, w której:

Bo ja proste systemy piszę :P

Taki przykładowy kawałek rzeczywistości, z jakim można się spotkać podczas realizowania zleceń nic wybitnie skomplikowanego a jednocześnie nie kilka trywialnie powiązanych encji.

Projektuje wszystko tak jak w OOP tylko tworzę niemutowalne obiekty bez dziedziczenia pół. Jak mamy zaprojektowane niemutowalne obiekty bez dziedziczenia pól to w Haskellu:

  • interfajs to TypeClassa (w Ruscie trait)
  • obiekt to struktura i moduł działający na tej strukturze

Jak mnie meczy zapis f(x) jak nie którzy robią tu z tego niesamowity problem to używam operatora pipe |> i mam zapis

myObject |> (myMethod1 param11) |> (myMethod2 param21 param22)

Oczywiście jak już trzeba coś zalogować bokiem, przetrzymać stan, przetrzymać globalny stan lub zrobic komunikację ze swiatem zewnętrznym to na to Haskell ma rozwiązanie w postaci Writer, State, IORef, IO. No, ale ot już potem kolejne szczegóły implementacyjne

A jak już to wszystko się ułoży to klient powie, że w zależności od stanowiska:

W zasadzie to nie wiem o co ci tu chodzi?

  • O to czy w Haskellu mogę czytać z bazy danych? Mogę
  • O to czy w Haskellu mogę walnąc 30 implementacji jednego interfejsu? Mogę
  • O to czy w Haskellu mogę walnąć ifa na 1500 przypadków jak w Javie? Mogę
0

@KamilAdam: no właśnie o to mi chodziło... Projektuje wszystko tak jak w OOP. Czyli nawet jeśli implementacyjne czerpiesz korzyści z paradygmatu funkcyjnego to obiektowe podejście w rzeczywistym świecie nie jest całkowicie do wykluczenia lub byłoby to bardzo karkołomne?
W implementacji zagadnień, matematycznych fizycznych, astronomicznych, ogólnie rzecz ujmując dla matematyków i fizyków, przetwarzania danych (np. zdjęć lub audio) pewnie podejście funkcyjne będzie bardziej naturalne niż OOP ale w zetknięciu z biznesem, firmami zwykłym klientem, który chce ogarnąć swoją firmę i trzeba z nim jakoś rozmawiać wg mnie lepiej pozostać w OOP.

2
4w0rX4t4X napisał(a):

@KamilAdam: no właśnie o to mi chodziło... Projektuje wszystko tak jak w OOP. Czyli nawet jeśli implementacyjne czerpiesz korzyści z paradygmatu funkcyjnego to obiektowe podejście w rzeczywistym świecie nie jest całkowicie do wykluczenia lub byłoby to bardzo karkołomne?

Pytanie tylko czy ty projektujesz OOP czy po prostu projektujesz? Bo jeśli na podstawie tego samego projektu można zrobić implementację OOP i FP to czy ten projekt ma w sobie coś szczególnego że możesz go nazwać projektowaniem OOP? Może to jest po prostu projektowanie?

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