Wątek przeniesiony 2022-06-21 17:19 z Kosz przez Adam Boduch.

Strategia w programowaniu obiektowym

2

@Riddle:

Riddle napisał(a):

Jezus maria, no przecież cały wątek jest o tym.

Dlatego narusza OOP, bo nie enkapsuluje żadnego stanu w sobie (nie dostaje nic przez parametry konstruktora), w efekcie nie różni się od lamdy/callbacka.

No i z faktu, że robisz implementację czegoś wysnuwasz wnioski, że wzorzec strategii nie/jest OOP? Rozróżniasz wzorzec (podejście do rozwiązania problemu) od implementacji (konkretne rozwiązanie)?
Może brak zrozumienie tej różnicy stanowi problem z irytacją i właściwym postawieniem zagadnienia?

Tak jakby rozważać problem "podróży z punktu A do punktu B" z wzorcem "możliwość zmiany środka lokomocji w trakcie podróży", ograniczeniami "środek lokomocji z kołami" i konkretną implementacją środka lokomocji "rower w kolorze zielonym".

Jeśli robisz przesiadkę w trakcie podróży z A do B na rower i zielony lakier ramy (bezstanowa implementacja) nie pasuje Ci do paradygmatu podróżowania, ok. To jednak nie jest powód by mówić, że zielony nie pasuje do paradygmatu. Znaczy, nikt Ci nie broni, ale ma to mało sensu. W każdym razie tak to wygląda z mojej perspektywy.

1
Riddle napisał(a):

Wykazałeś że nie koniecznie musi się dać, ale nie wykazałeś że sie nie da.

Tyle tylko, że ja nie wykazywałem, że się nie da. Ja wykazywałem, że twoje stwierdzenie:

Jeśli stragia może nie przyjmować żadnych wartości i nadal być obiektowa, to znaczy to że wszystkie klasy mogłyby nie enkaosulować nic, i nadal być obiektowe.
A skoro tak, to można by napisać całą aplikacje nie enkapsulujac nic, i nazwać ją obiektową.

jest błędem logicznym.

W twoim przypadku - "jest obiektowa" to A, "nie enkapsuluje" to B, a "cała aplikacja" to C.

Jeśli możesz napisać C za pomocą elementów A, i jednocześnie wiesz, że B jest podzbiorem zbioru A, to nie wiesz, czy da się złożyć C z elementów B. Ponieważ to ty podniosłeś to stwierdzenie to ciężar dowodu leży po twojej stronie.

0
yarel napisał(a):

@Riddle:

Riddle napisał(a):

Jezus maria, no przecież cały wątek jest o tym.

Dlatego narusza OOP, bo nie enkapsuluje żadnego stanu w sobie (nie dostaje nic przez parametry konstruktora), w efekcie nie różni się od lamdy/callbacka.

No i z faktu, że robisz implementację czegoś wysnuwasz wnioski, że wzorzec strategii nie/jest OOP? Rozróżniasz wzorzec (podejście do rozwiązania problemu) od implementacji (konkretne rozwiązanie)?
Może brak zrozumienie tej różnicy stanowi problem z irytacją i właściwym postawieniem zagadnienia?

Tak jakby rozważać problem "podróży z punktu A do punktu B" z wzorcem "możliwość zmiany środka lokomocji w trakcie podróży", ograniczeniami "środek lokomocji z kołami" i konkretną implementacją środka lokomocji "rower w kolorze zielonym".

Jeśli robisz przesiadkę w trakcie podróży z A do B na rower i zielony lakier ramy (bezstanowa implementacja) nie pasuje Ci do paradygmatu podróżowania, ok. To jednak nie jest powód by mówić, że zielony nie pasuje do paradygmatu. Znaczy, nikt Ci nie broni, ale ma to mało sensu. W każdym razie tak to wygląda z mojej perspektywy.

Mógłbyś się wyrazić bez metafory, i przejść do rzeczy?

Albo daj arugmenty za tym że jeśli widzisz użyty wzorzec strategia, to można powiedzieć że ten kawałek kodu jest w paradygmacie OOP; albo nie. Jeśli nie masz argumentów ani za ani przeciw, to dziękuję za udział.

wartek01 napisał(a):

Jeśli możesz napisać C za pomocą elementów A, i jednocześnie wiesz, że B jest podzbiorem zbioru A, to nie wiesz, czy da się złożyć C z elementów B. Ponieważ to ty podniosłeś to stwierdzenie to ciężar dowodu leży po twojej stronie.

Okej. Jeśli mam 100 klas, które wiem że są w 100% obiektowe, i jedną strategię, która czegoś nie enkapsuluje a nadal jest obiektowa, to czemu miałbym nie zrobić programu w którym 99 klas jest pure oop, a dwie strategie czegoś nie enakapsulują? Albo 98 i 3? Albo 97 pure OOP i 4 strategie. Gdzie jest granica?

Albo gdzieś jest granica, I pewnym momencie można powiedzieć czemu strategia jest albo nie jest w paradygmacie OOP; albo cały program oparty na na klasach które niczego nie enkapsulują cały jest w OOP.

@warker01 nie mam zamiaru dłużęj rozmawiać nt logiki - nie interesuje mnie to. Wiem, że mógłbyś wziąć pytanie z wątku, zamienić je w logiczną zagadkę, i wyciągnąć dowolne wnioski - nie interesuje mnie to.

Mnie interesuje TYLKO I WYŁĄCZNIE pytanie "Czy strategia wpisuje się we wzorzec obiektowy", tylko i wyłącznie. Jeśli chcesz podać argument, podaj argument programistyczny. Dziękuję.

Strategia albo jest zgodna z paradygmatem, albo nie jest. Nie ma czegoś takiego jak "bycie w połowie zgodne z paradygmatem". Więc jest czy nie jest?

0
Riddle napisał(a):

Kiedy mówię "enkaosulacja" mam na myśli hermetyzację parametrów, wartości danych, nie zachowania.

Czyli wymyślasz sobie swoją błędną definicję enkapsulacji, potem używasz jej jako cechy, o której mowa w definicji OOP dopowiadając sobie jej rzekomą obowiązkowość, i na tej podstawie próbujesz ustalić, że coś jest albo nie jest OOP? Powodzenia. :P

W ten sposób, to można wszystko "udowodnić"". Ludzie odżywają się przez fotosyntezę, @Riddle nie odżywia się przez fotosyntezę, @Riddle nie jest człowiekiem.

A skoro tak, to można by napisać całą aplikacje nie enkapsulujac nic, i nazwać ją obiektową.

A to mi się nie wydaje prawdziwe.

Bo? Aplikacja aby była obiektowa musi mieć obiekty. Reszta to cechy wynikające z dobrego projektowania.

Riddle napisał(a):

@piotrpo: Wypowiadasz się nie na temat. Pozwól że zadam pytanie z wątku jeszcze raz, licząc że może teraz zrozumiesz o co chodzi.

Przykładowy kod używający strategi

interface Collection {
  int size();
  bool empty();
  sort(SortStrategy strategy);
}

interface SortStrategy {
  public sort(Collection c);
}

i jej użycie

c = new ArrayCollection();
c.sort(new BubbleSort()); // sortowanie bąbelkowe
c.sort(new QuickSort()); // sortowanie quick-sort

Ktoś użył wzorca Strategy, żeby dostarczyć dwie implementacje sortowania do kolekcji. Całą intencją istnienia tego interfejsu jest to, żeby dostarczyć różne implementacje, i zostały zaprojektowane z myślą o tym żeby te strategie były niczym innym jak tylko implementacją algorytmu bez niczego w środku.

Ten kod nie ma za bardzo związku ze wzorcem strategii. We wzorcu oprócz interfejsu i konkretnych implementacji masz też obiekt używający konkretnej implementacji algorytmu oraz obiekt, który wybiera i przekazuje konkretną implementację do kodu używającego i może to robić w locie.

0
somekind napisał(a):
Riddle napisał(a):

Kiedy mówię "enkaosulacja" mam na myśli hermetyzację parametrów, wartości danych, nie zachowania.

Czyli wymyślasz sobie swoją błędną definicję enkapsulacji, potem używasz jej jako cechy, o której mowa w definicji OOP dopowiadając sobie jej rzekomą obowiązkowość, i na tej podstawie próbujesz ustalić, że coś jest albo nie jest OOP? Powodzenia. :P

Tak mi się wydawało, że tym jest enkapsulacja. Mogę też zadać pytanie nie używając tego słowa/kategorii, jesli budzi taką kontrowersję:

"Czy strategia pasuje do paradygmatu obiektowego?". Pytam, dlatego że uważam, że w paradygmacie obiektowym, obiekty powinny dostać coś przez parametr konstruktora. A uważam tak, dlatego że jeśli nie dostają czegoś, to można by zrobić z nich funkcje, po prostu, więc nie byłoby róznicy czy przekażę implementację interfejsu czy callbacka (a nie uważam żeby callbacki się wpisywały w paradygmat obiektowy). A po drugie dlatego, że wtedy nie byłoby znaczenia czy użyję tych samych instancji jakiejś implementacji czy różnych. Nie wydaje mi się że te dwie ostatnie cechy pasują do paradygmatu, a jednocześnie widzę że strategie je mają stąd pytanie - "Czy strategia pasuje do paradygmatu obiektowego?"

Mam nadzieję że teraz się wyraziłem jasno.

A skoro tak, to można by napisać całą aplikacje nie enkapsulujac nic, i nazwać ją obiektową.

A to mi się nie wydaje prawdziwe.

Bo? Aplikacja aby była obiektowa musi mieć obiekty. Reszta to cechy wynikające z dobrego projektowania.

Czyli aplikacja posiadająca obiekty, ale która cześć logiki ma np w funkcjach statycznych jest obiektowa?

Według mnie nie.

Riddle napisał(a):

@piotrpo: Wypowiadasz się nie na temat. Pozwól że zadam pytanie z wątku jeszcze raz, licząc że może teraz zrozumiesz o co chodzi.

Przykładowy kod używający strategi

interface Collection {
  int size();
  bool empty();
  sort(SortStrategy strategy);
}

interface SortStrategy {
  public sort(Collection c);
}

i jej użycie

c = new ArrayCollection();
c.sort(new BubbleSort()); // sortowanie bąbelkowe
c.sort(new QuickSort()); // sortowanie quick-sort

Ktoś użył wzorca Strategy, żeby dostarczyć dwie implementacje sortowania do kolekcji. Całą intencją istnienia tego interfejsu jest to, żeby dostarczyć różne implementacje, i zostały zaprojektowane z myślą o tym żeby te strategie były niczym innym jak tylko implementacją algorytmu bez niczego w środku.

Ten kod nie ma za bardzo związku ze wzorcem strategii. We wzorcu oprócz interfejsu i konkretnych implementacji masz też obiekt używający konkretnej implementacji algorytmu oraz obiekt, który wybiera i przekazuje konkretną implementację do kodu używającego i może to robić w locie.

Wow. W końcu jakaś sensowna odpowiedź.

Czyli rozumiem że mówisz że kod który pokazałem, tak na prawdę nie prezentuje strategii?

No dobrze, to zakładając że byłby jakiś obiekt używający konkretnego algorytmu, czy wtedy ten interfejs SortStrategy byłby widoczny poza tym obiektem?

No dobrze, no to załózmy że jes tak. Czy wtedy taka strategia byłaby napisana w paradygmacie obiektowym?

0
somekind napisał(a):
Riddle napisał(a):

Przykładowy kod używający strategi

interface Collection {
  int size();
  bool empty();
  sort(SortStrategy strategy);
}

interface SortStrategy {
  public sort(Collection c);
}

i jej użycie

c = new ArrayCollection();
c.sort(new BubbleSort()); // sortowanie bąbelkowe
c.sort(new QuickSort()); // sortowanie quick-sort

Ktoś użył wzorca Strategy, żeby dostarczyć dwie implementacje sortowania do kolekcji. Całą intencją istnienia tego interfejsu jest to, żeby dostarczyć różne implementacje, i zostały zaprojektowane z myślą o tym żeby te strategie były niczym innym jak tylko implementacją algorytmu bez niczego w środku.

Ten kod nie ma za bardzo związku ze wzorcem strategii. We wzorcu oprócz interfejsu i konkretnych implementacji masz też obiekt używający konkretnej implementacji algorytmu oraz obiekt, który wybiera i przekazuje konkretną implementację do kodu używającego i może to robić w locie.

A teraz:

val bubbleSort = new BubbleSort()
val quickSort = new QuickSort()

val c = new ArrayCollection()

val sortingAlgorithm  = if (doFast) quickSort else quickSort //tu wybieram, czy tego brakowało?
c.sort(sortingAlgorithm)

?

0
KamilAdam napisał(a):

A teraz:

val bubbleSort = new BubbleSort()
val quickSort = new QuickSort()

val c = new ArrayCollection()

val sortingAlgorithm  = if (doFast) quickSort else quickSort //tu wybieram, czy tego brakowało?
c.sort(sortingAlgorithm)

?

No właśnie też się zastanawiam, ale chyba nie o to chodziło @somekind.

Myślę że chodziło mu o coś bardziej w takim stylu

class Something {
  Something() {
    this.collection = new ArrayCollection();
  }

  sort() {
    if (something()) {
      this.collection(new BubbleSort());
    } else {
      c.sort(new QuickSort());
    }
  }

  interface Collection {
    int size();
    bool empty();
    sort(SortStrategy strategy);
  }
  
  interface SortStrategy {
    public sort(Collection c);
  }
}

Tylko własnie nie wiem co tutaj jest kluczem.

@somekind: Chodzi o to że strategia nie jest widoczna poza obiektem?

1

IMO @somekind owi coś się poplątało - o tu:

We wzorcu oprócz interfejsu i konkretnych implementacji masz też obiekt używający konkretnej implementacji algorytmu oraz obiekt, który wybiera i przekazuje konkretną implementację do kodu używającego i może to robić w locie.

Istotą strategii jest to, że klient może wybrać strategię.

Natomiast faktycznie - gdzieś ten wybór w końcu jest. Również w każdym przypadku HOF. Te funkcje (argumenty) się magicznie nie biorą, ktoś je jednak gdzieś wybiera.

0
jarekr000000 napisał(a):

IMO @somekind owi coś się poplątało - o tu:

We wzorcu oprócz interfejsu i konkretnych implementacji masz też obiekt używający konkretnej implementacji algorytmu oraz obiekt, który wybiera i przekazuje konkretną implementację do kodu używającego i może to robić w locie.

Istotą strategii jest to, że klient może wybrać strategię.

Natomiast faktycznie - gdzieś ten wybór w końcu jest. Również w każdym przypadku HOF. Te funkcje (argumenty) się magicznie nie biorą, ktoś je jednak gdzieś wybiera.

Strategia też może być całkowitym szczegółem implementacyjnym, w taki sposób że klient w ogóle nie wie czy coś było ifem czy strategią.

0
Riddle napisał(a):

"Czy strategia pasuje do paradygmatu obiektowego?". Pytam, dlatego że uważam, że w paradygmacie obiektowym, obiekty powinny dostać coś przez parametr konstruktora. A uważam tak, dlatego że jeśli nie dostają czegoś, to można by zrobić z nich funkcje, po prostu, więc nie byłoby róznicy czy przekażę implementację interfejsu czy callbacka (a nie uważam żeby callbacki się wpisywały w paradygmat obiektowy). A po drugie dlatego, że wtedy nie byłoby znaczenia czy użyję tych samych instancji jakiejś implementacji czy różnych. Nie wydaje mi się że te dwie ostatnie cechy pasują do paradygmatu, a jednocześnie widzę że strategie je mają stąd pytanie - "Czy strategia pasuje do paradygmatu obiektowego?"

No dobra, ale czy jeśli mam aplikację, w której mam tysiąc klas, które dostały coś przez parametr konstruktora, i jedną, która nie, to to już nie będzie kod obiektowy? Czy może jeśli język pozwala na tworzenie bezparametrowych konstruktorów, nie jest językiem obiektowym?

Czyli aplikacja posiadająca obiekty, ale która cześć logiki ma np w funkcjach statycznych jest obiektowa?

No to w takim razie żadna aplikacja nie jest obiektowa.

Tylko własnie nie wiem co tutaj jest kluczem.

@somekind: Chodzi o to że strategia nie jest widoczna poza obiektem?

Obiekty są 3:

  1. Jeden główny, który chce coś zrobić, i wie jakim algorytmem chce to zrobić. On tworzy/wybiera obiekt konkretnej implementacji algorytmu, i przekazuje go dalej.
  2. Drugi, który trzyma referencję do konkretnej implementacji algorytmu, dostaje ją np. w konstruktorze i używa w swoim flow.
  3. No i wreszcie obiektami są konkretne implementacje algorytmu.
    Między nimi gdzieś tam poniewiera się interfejs, aby to wszystko połączyć, bo taką mamy składnię.

Tak więc, strategia nie jest widoczna poza obiektem 1, a używa jej obiekt 2. W Twoim kodzie brakuje któregoś z nich.

jarekr000000 napisał(a):

IMO @somekind owi coś się poplątało - o tu:

We wzorcu oprócz interfejsu i konkretnych implementacji masz też obiekt używający konkretnej implementacji algorytmu oraz obiekt, który wybiera i przekazuje konkretną implementację do kodu używającego i może to robić w locie.

Istotą strategii jest to, że klient może wybrać strategię.

I gdzie tu jest to moje poplątanie? Obiekt, który wybiera i przekazuje konkretną implementację algorytmu do użycia, to jest właśnie klient.

0
somekind napisał(a):

Istotą strategii jest to, że klient może wybrać strategię.

I gdzie tu jest to moje poplątanie? Obiekt, który wybiera i przekazuje konkretną implementację algorytmu do użycia, to jest właśnie klient.

No i kłócisz się, że to nie jest HOF. To jest HOF (modulo interfejs(klasa abstrakcyjna)-funkcja). Przy czym
Strategia jest HOF,
Nie każde HOF jest strategią.

0
jarekr000000 napisał(a):

No i kłócisz się, że to nie jest HOF. To jest HOF (modulo interfejs(klasa abstrakcyjna)-funkcja). Przy czym
Strategia jest HOF,
Nie każde HOF jest strategią.

Co najwyżej strategia być może zawiera w sobie HOF.

Bo tak poza tym, to ani jej celem ani ideą nie jest zawieranie w sobie HOF (ani nawet interejsu), więc nie bardzo jest sens tak bardzo się na tym skupiać. :)

0

@Riddle:

Nie ma żadnego powodu zeby uważać że obiekty new BubbleSort() i new QuickSort() faktycznie są w paradygmacie obiektowym. Równie dobrze mogłyby być funkcjami.

Jak nie ma?

Jest - taka interpretacja jest spójna z definicją Object-oriented programming (OOP) is a programming paradigm based on the concept of "objects", which can contain data and code: data in the form of fields (often known as attributes or properties), and code, in the form of procedures (often known as methods).


W ogóle jak już w temacie enkapsulacji, to dlaczego ty tak zaglądasz do tych bebechów?

Przecież jeżeli uznasz że takie klasy bez danych faktycznie nie mają sensu i zaczniesz zmieniać np.

class SortA : ISortStrategy
{
	public sort(Collection c);
}

na funkcje (Równie dobrze mogłyby być funkcjami.)

to nagle tracisz elastyczność, bo za pół roku będzie Ci ciężej rozszerzyć/zmienić zachowanie na takie, aby w dodatku coś przechowywało pod spodem.

class SortA : ISortStrategy
{
    private _cache[Collection key, Collection value];
    private EnvironmentDetails _env; // cpu and environment detected on very first run
	public sort(Collection c);
    private internalEnvironmentAwareSort(Collection c);
}

Musiałbyś zmienić sygnaturę z funkcji na coś innego

lub potworka tego typu:

public class User 
{
    public bool Checked = false;
    public void DoStuff()
    {
        Checked = true;
    }
}

public static void Test(Action a)
{
    a();
}

public static void Main()
{
    var user = new User();
    Test(user.DoStuff);
    Console.WriteLine(user.Checked);
}

Czyli różne języki mają różne paradgymaty obiektowe?

Tak, różne języki mogą interpretować, a zatem implementować OOP inaczej.

Moim zdaniem przykładem tego jest chociażby friend z c++.

1

Ja tu widzę podstawowy problem. To, że istnieją bezstanowe strategie (typu BubbleSort, QuickSort), które przypominają lambdy nie oznacza jeszcze, że łamią one obiektowość. W takiej Javie rozwiązane jest to tak, że stworzona w ten sposób strategia jest obiektem klasy anonimowej implementująca jakiś tam interfejs. W tym przypadku paradygmat obiektowy byłby złamany gdyby z tak stworzoną strategią nie można było zrobić tego samego, co z dowolnym innym obiektem - tj. skopiować referencji, sprawdzić jakiej jest klasy itp. Na tym polega właśnie paradygmat obiektowy.

I teraz można się zastanawiać, czy np. w językach funkcyjno-obiektowych (tj. takich, który pozwala jednocześnie na tworzenie klas obiektów, ale także można przypisywać funkcje do zmiennych) zastosowanie wzorca strategii nie złamie paradygmatu obiektowego - ale to nie jest problem z samym wzorcem strategii, tylko od implementacji i języka.

0

@jarekr000000, @somekind
Nie jestem pewien, czy Strategia może być HOF. To już czysta sofistyka, ale...
Wzorzec projektowy strategii, to sposób na rozwiązanie określonej klasy problemów, czyli zmiany działania fragmentu programu, w trakcie jego wykonywania, sterowana przez coś z zewnątrz. Oczywiście taki problem występuje niezależnie od języka.
Jednak wzorzec, to nie tylko definicja problemu, ale również zalecane rozwiązanie, które wygląda tak:
screenshot-20220623083535.png
Źródło: https://refactoring.guru/design-patterns/strategy

Pytanie, czy jeżeli klasę Navigator zastąpimy HOF, RouteStrategy zniknie, a konkretne strategie staną się innymi funkcjami przekazywanymi do HOF jako jej parametry, to czy takie rozwiązanie, nadal pozostanie strategią? Czy statek Tezeusza staje się nowym statkiem po wymianie pierwszej części, wszystkich części, czy nigdy?

0
piotrpo napisał(a):

Źródło: https://refactoring.guru/design-patterns/strategy>
Pytanie, czy jeżeli klasę Navigator zastąpimy HOF, RouteStrategy zniknie, a konkretne strategie staną się innymi funkcjami przekazywanymi do HOF jako jej parametry, to czy takie rozwiązanie, nadal pozostanie strategią? Czy statek Tezeusza staje się nowym statkiem po wymianie pierwszej części, wszystkich części, czy nigdy?

Wreszcie jakiś konkret. Tam na dole jest też pseudo kod. W Scali z HOF ten pseudokod będzie wyglądać tak:

object Strategy extends App {
  type Strategy = (Int, Int) => Int //alias typu dpa picu

  // strategie można by też zdefiniowac jako lambdy, ale wolałem jako *statyczne* metody
  def concreteStrategyAdd(a: Int, b: Int): Int = a + b
  def concreteStrategySubtract(a: Int, b: Int): Int = a - b
  def concreteStrategyMultiply(a: Int, b: Int): Int = a * b

  // brzydki konteks przyjmujący nulla w konstruktorze
  class Context(var strategy: Strategy = null) {
    def executeStrategy(a: Int, b: Int): Int = strategy(a, b)
  }
  
  val context = new Context()
  val first = scala.io.StdIn.readInt
  val last = scala.io.StdIn.readInt
  val action = scala.io.StdIn.readChar

  val strategy = action match {
    case '+' => concreteStrategyAdd _
    case '-' => concreteStrategySubtract _
    case '*' => concreteStrategyMultiply _
  }

  // w produkcyjnym kodzie bym oczywiście czegoś takiego nie zrobił
  context.strategy = strategy
  //wywołanie strategii
  val result = context.executeStrategy(first, last)
  println(result)
}

WIem że to bardzo brzydki kod jak na Scalę (null parameter, nie pełny match), ale nie chciałem zmieniać za dużo

BTW interfejs Stretegii z przykładu Real World Analogy wygląda jak upośledzona funkcja :P

Real World Analodzy

UPDATE ponieważ nie mogłem się powstrzymać to jeszcze rozwiązanie z niemutowalnym kontekstem który też jest funkcją :P

object Strategy extends App {
  type Strategy = (Int, Int) => Int

  // strategie jako lambdy
  val concreteStrategyAdd: Strategy = (a, b) => a + b
  val concreteStrategySubtract: Strategy = (a, b) => a - b
  val concreteStrategyMultiply: Strategy = (a, b) => a * b

  // ładny kontekst, tylko że nic nie robi
  // mogłby też być metodą *statyczną*
  // def Context(strategy: Strategy)(a: Int, b: Int): Int = strategy(a, b)
  class Context(val strategy: Strategy) {
    def apply(a: Int, b: Int): Int = strategy(a, b)
  }

  val first = scala.io.StdIn.readInt
  val last = scala.io.StdIn.readInt
  val action = scala.io.StdIn.readChar

  val strategy = action match {
    case '+' => concreteStrategyAdd
    case '-' => concreteStrategySubtract
    case '*' => concreteStrategyMultiply
  }

  // jeśli kontekst byłby metodą statyczną to
  // val context = Context(strategy) _
  val context = new Context(strategy)
  val result = context(first, last)
  println(result)
}
0
somekind napisał(a):
Riddle napisał(a):

"Czy strategia pasuje do paradygmatu obiektowego?". Pytam, dlatego że uważam, że w paradygmacie obiektowym, obiekty powinny dostać coś przez parametr konstruktora. A uważam tak, dlatego że jeśli nie dostają czegoś, to można by zrobić z nich funkcje, po prostu, więc nie byłoby róznicy czy przekażę implementację interfejsu czy callbacka (a nie uważam żeby callbacki się wpisywały w paradygmat obiektowy). A po drugie dlatego, że wtedy nie byłoby znaczenia czy użyję tych samych instancji jakiejś implementacji czy różnych. Nie wydaje mi się że te dwie ostatnie cechy pasują do paradygmatu, a jednocześnie widzę że strategie je mają stąd pytanie - "Czy strategia pasuje do paradygmatu obiektowego?"

No dobra, ale czy jeśli mam aplikację, w której mam tysiąc klas, które dostały coś przez parametr konstruktora, i jedną, która nie, to to już nie będzie kod obiektowy? Czy może jeśli język pozwala na tworzenie bezparametrowych konstruktorów, nie jest językiem obiektowym?

Ja bym wtedy powiedział, że 99% aplikacji jest napisanej obiektowo, i ma jedną proceduralną klasę.

Podobnie jak masz 99% coverage'a, to nikt nie powie że kod niej est przetestowany, ale nie można też powiedzieć żę jest w pełni przetestowany.

Jeśliby poprawić tą klasę na obiektową, to wtedy powiedziałbym że aplikacja cała jest w pełni obiektowa. Podobnie, gdybym odpalił mutaion testing engine, i on by zapił 100% mutatnów, to powiedziałbym żę aplikacja jest w pełni przetestowana.

Co do języka: Już bardzo dawno przesatłem używać takich określeń jak "obiektowy język", "funkcyjny język", nie mają takie określenia sensu dla mnie. Można pisać obiektowo w nieobiektowym języku. Takie określenia dla mnie straciły już sens. Metafora, nawet jak znajdę wegetariańskie danie, to łatwo zrobić je niewegetariańskim, tak samo łatwo jak napisać nieobiektowy kod w rzekomo obiektowym języku.

Ja teraz nie posługuję się w ogóle określeniami "obiektowy język", i mówię tylko "obiektowy kod" albo "nie obiektowy kod". Nie ma języka który by Cię zmusił żebyś uniezależnił dane od implementacji, zawsze jak się uprzesz, to złamiesz gdzieś paradygmat.

Czyli aplikacja posiadająca obiekty, ale która cześć logiki ma np w funkcjach statycznych jest obiektowa?

No to w takim razie żadna aplikacja nie jest obiektowa.

Niektóre są. Mogę sobie łatwo wyobrazić aplikacje która nie ma absolutnie żądnej logiki w funkcjach statycznych, i spełniająca pozostałe założenia.

Tylko własnie nie wiem co tutaj jest kluczem.

@somekind: Chodzi o to że strategia nie jest widoczna poza obiektem?

Obiekty są 3:

  1. Jeden główny, który chce coś zrobić, i wie jakim algorytmem chce to zrobić. On tworzy/wybiera obiekt konkretnej implementacji algorytmu, i przekazuje go dalej.
  2. Drugi, który trzyma referencję do konkretnej implementacji algorytmu, dostaje ją np. w konstruktorze i używa w swoim flow.
  3. No i wreszcie obiektami są konkretne implementacje algorytmu.
    Między nimi gdzieś tam poniewiera się interfejs, aby to wszystko połączyć, bo taką mamy składnię.

Tak więc, strategia nie jest widoczna poza obiektem 1, a używa jej obiekt 2. W Twoim kodzie brakuje któregoś z nich.

Czyli możnaby ją zrefaktorować do innego rozwiązania, np ifów? Albo innego dowolnego? I klienci tej klasy 1. by nie wiedzieli? Bo rozumiem że klasy 2. nikt nie używa bezpośrednio, a jedynie za pośrednictwej klasy 1.?

2

Zauważam podobne reakcje, jak tłumaczę komuś coś związanego z paradygmatem obiektowym, do innych dyscyplin.

Dam przykład może.

  • MVC - Powiedz komuś że w MVC chodzi o to, żeby usunąć wszystkie powiązania widoku z modelem. Co odpowie?

    • Ale to nie ma sensu! Jak w taki sposób coś wyświetlić? Byłby biały ekran! To nie ma sensu
  • Functional programming - Powiedz komuś, żę w FP nie można redefiniować raz ustawionych zmiennych. Co odpowie?

    • Ale to nie ma sensu! Jak wtdy zrobić fora? Wszystkie zmienne byłyby takie same, program byłby statyczny! To nie ma sensu!
  • TDD - Powiedz komuś, że nie może napisać żadnego kodu przed testem, co odpowiadają?

    • Ale musze napisać program, żeby móc go przetestować. Co mam niby przetestować? To nie ma sensu!
  • Na dokładkę może funkcje abstrakcyjne - Uczymy kogoś polimorfizmu (tego na klasach i interfejsach)

    • Ale po co mam deklarować funkcję bez ciała, która nic nie robi? To nie ma sensu.

Na pewno doświadczyliście takich odpowiedzi, kiedy przekazywanie komuś jakiejś dyscypliny, ale w taki wydestylowany sposób, jak np mówienie o TDD przez pryzmat trzech praw, albo o paradygmatach przez pryzmat dogmatów, to ktoś może dojść do wniosku że każda z tych dyscyplin nie ma sensu, jak się tak po prostu o niej słyszy.

Co nam to mówi?

Może to, że jeśli spotykamy się z jakąś dyscypliną, to może nie brać jej przez intuicję i próbować "się ją naumieć bez wysiłku", tylko może jednak przez pryzmat doświadczenia i wiedzy, i wiedzieć że żęby poznać nową dyscyplinę to to wymaga żeby włożyć w to jakiś minimum wysiłku żeby to zrozumieć, czasem całkiem sporo.

3

Bo uważam że w obiektowym świecie obiekty powinny przyjmować wartości przez konstruktor (coś co nazwałem enkapsulacją, co potem zrodziło gównoburzę)

Gdyby tak było, to każdy jeden język wymagałby tworzenia klasy z obowiązkowym konstruktorem z parametrami, ale tak nie jest. To wszystko jest opcjonalne i nadal są to obiekty, czyli dziedziczenie, wstrzykiwanie itd. stan obiektu można zmieniać/ustawiać nie tylko za pomocą konstruktora.
Uroiłeś sobie jakąś wizję programowania obiektowego i się jej kurczowo trzymasz, mimo, że jest nieprawdziwa.

0
omenomn2 napisał(a):

Bo uważam że w obiektowym świecie obiekty powinny przyjmować wartości przez konstruktor (coś co nazwałem enkapsulacją, co potem zrodziło gównoburzę)

Gdyby tak było, to każdy jeden język wymagałby tworzenia klasy z obowiązkowym konstruktorem z parametrami, ale tak nie jest. To wszystko jest opcjonalne i nadal są to obiekty, czyli dziedziczenie, wstrzykiwanie itd. stan obiektu można zmieniać/ustawiać nie tylko za pomocą konstruktora.
Uroiłeś sobie jakąś wizję programowania obiektowego i się jej kurczowo trzymasz, mimo, że jest nieprawdziwa.

A Ty znowu swoje.

Programowanie obiektowe to nie jest "po prostu używanie klas", wbij sobie to do głowy. To jest paradygmat. Możesz używać klas, obiektów i dziedziczenia na dziesiątą stronę, a i tak stworzyć kod który nie jest obiektowy.

Samo używanie funkcji, nie sprawia że piszesz funkcyjnie - samo używanie obiektów i klas nie sprawia że piszesz obiektowo.

Dziękuję za wzięcie udziału w rozmowie.

4

Programowanie obiektowe to nie jest "po prostu używanie klas", wbij sobie to do głowy. To jest paradygmat. Możesz używać klas, obiektów i dziedziczenia na dziesiątą stronę, a i tak stworzyć kod który nie jest obiektowy.

Tylko, że Ty ten paradygmat, który tak wielbisz, po prostu źle interpretujesz.
Nie jest używaniem klas, jest używaniem obiektów.
Obiekt nie musi dostać nic w konstruktorze i nadal jest to programowanie obiektowe.

0
jarekr000000 napisał(a):
Riddle napisał(a):

Kod wtedy jest obiektowy, kiedy dane przekazywane pomiędzy różnymi, bliskimi poziomami abstrakcji (i nie mam tu na myśli metod abstrakcyjnych), może być przedstawiona conajmniej jednym obiektem, który przyjmuje ten dane przez konstruktor.

@Riddle: Czyli każdy kod jest obiektowy? Czy też masz kontrprzykład kiedy dane przekazywane pomiędzy różnymi, bliskimi poziomami abstrakcji (i nie mam tu na myśli metod abstrakcyjnych), NIE MOGĄ być przedstawiona conajmniej jednym obiektem, który przyjmuje ten dane przez konstruktor.

To moim zdaniem jest przykład kodu który nie pasuje do tej definicji

def getMimeType(filename: str)->str:
    extension = getExtension(filename)
    if extension in ['jpg', 'jpeg', 'png', 'gif']:
        return 'image/'
    return 'application/x-'

def getExtension(filename:str) -> str:
    return # tutaj jakiś kod który wyciąga ostatnie znaki po kropce

Bo z poziomu abstrakcji "mime-type" do "filename oraz extension" przechodzisz stringiem, bez obiektu pomiedzy nimi.

Obiektowo byłoby tak

def getMimeType(filename: Filename)->str:
    if filename.extension() in ['jpg', 'jpeg', 'png', 'gif']:
        return 'image/'
    return 'application/x-'

class Filename:
  def getExtension() -> str:
    return # tutaj jakiś kod który wyciąga ostatnie znaki po kropce
1

@Riddle
No chwilę

  1. String teź jest obiektem.
  2. Skoro zrobiłeś wersję drugą gdzie jest ta klasa zkonstruktorem to okazało się, że może być przedstawiona conajmniej jednym obiektem, który przyjmuje ten dane przez konstruktor. - czyli kod pierwszy jest obiektowy.
2
Riddle napisał(a):
def getMimeType(filename: str)->str:
    extension = getExtension(filename)
    if extension in ['jpg', 'jpeg', 'png', 'gif']:
        return 'image/'
    return 'application/x-'

def getExtension(filename:str) -> str:
    return # tutaj jakiś kod który wyciąga ostatnie znaki po kropce

Bo z poziomu abstrakcji "mime-type" do "filename oraz extension" przechodzisz stringiem, bez obiektu pomiedzy nimi.

Co to za język? Python? Przecież w Pythonie (z tego co mi wiadomo) string też jest obiektem. Więc dlaczego obiekt string jest gorszy niż obiekt Filename?

0
jarekr000000 napisał(a):

@Riddle
No chwilę

  1. String teź jest obiektem.
KamilAdam napisał(a):

Co to za język? Python? Przecież w Pythonie (z tego co mi wiadomo) string też jest obiektem. Więc dlaczego obiekt string jest gorszy niż obiekt Filename?

Mówiąc "obiekt" nie mam na myśli tego czy jakiś język sobie coś nazwał obiektem czy nie.

Kiedy mówię "obiekt", mam na myśli obiekt w rozumieniu paradygmatu obiektowego.

Czy str w pythonie jest obiektem?

  • ie. Czy str można potraktować jako obiekt w rozumieniu elementu języka? Tak
  • ie. Czy str można potraktować jako obiekt w rozumieniu paradygmatu obiektowego? Nie

Kiedy mówię o OOP, i używam określenia "obiekt" to mam na myśli to że biorę jakąś daną, opakowuję ją w klasię przekazując ją przez konstruktor, i wystawiam operacje na niej jako funkcje - to mam na mysli mówiąć "obiekt w OOP".

To czy w filozofii jakiegoś języka coś jest obiektem czy nie, to po prostu zbierzność nazw.

0
Riddle napisał(a):
  • ie. Czy str można potraktować jako obiekt w rozumieniu paradygmatu obiektowego? Nie

Dlaczego?

0
KamilAdam napisał(a):
Riddle napisał(a):
  • ie. Czy str można potraktować jako obiekt w rozumieniu paradygmatu obiektowego? Nie

Dlaczego?

Gdybym po prostu miał przekazać stringa w dół, to jeszce spoko.

Ale tutaj w jednym miejscu traktujesz go jak coś co ma rozszerze, a w drugim jak coś co ma mime-type. Zbyt duże uzależnienie danych od operacji na nich. Nie obiektowe IMO.

Nie można powiedzieć jednoznacznie czy "jakaś klasa jest obiektowa czy nie jest", sama w sobie. To w kompozycji tych klas (conajmniej dwóch) widać obiektówkę. IMO

0
Riddle napisał(a):
KamilAdam napisał(a):
Riddle napisał(a):
  • ie. Czy str można potraktować jako obiekt w rozumieniu paradygmatu obiektowego? Nie

Dlaczego?

Bo nie opakowuje sobą żadnych danych, nie wystawia dostępu do nich na wyższym poziomie abstrakcji metodami.

Opakowuje np tablicę charów lub tablicę bajtów. (zależy od implementacji języka).

W tym drugim przypadku widać że dostarcza abstrakcję bo często (w Javie) str.length() jest różne od str.getBytes().length. Tą abstrakcją jest tutaj obsługa UTFa

1

Bo nie opakowuje sobą żadnych danych, nie wystawia dostępu do nich na wyższym poziomie abstrakcji metodami.

Czyli nie przyjmuje nic konstruktorze, niezła bzdura.
Czyli istnieją nieobiektowe obiekty, a niektóre samochody, są niesamochodowe, tak wynika z tego co pisze @Riddle .

Pomijając fakt, że połowa wzorców musiałaby być "nieobiektowa", bo nie tylko strategia nie wymaga wstrzyknięcia niczego w kostruktor.

Dekorator jest za to pół obiektowy, bo obiekt dekorowany nie przyjmuje nic do konstruktora, ale dekoratory już tak.

0
omenomn2 napisał(a):

Bo nie opakowuje sobą żadnych danych, nie wystawia dostępu do nich na wyższym poziomie abstrakcji metodami.

Czyli nie przyjmuje nic konstruktorze, niezła bzdura.
Czyli istnieją nie obiektowe obiekty, a niektóre samochody, są nie samochodowe, tak wynika z tego co pisze @Riddle .

Słuchaj! Ty myślisz o "kod obiektowy" w kontekście takim:

  • "Czy jest użyte słowo class w kodzie?" - jeśli tak, to według @omenomn2 kod jest obiektowy.

Możesz sobie tak uważać, ale nie o tym w ogóle jest ta rozmowa! Mówisz nie na temat. To jest tak samo bezsensowne, jak jeśli znajdziesz funkcję w kodzie (nie ważne czy ma side-effecty czy nie, to to jest kod funkcyjny).

Rozmowa tyczy sie programowania obiektowego w kontekscie PARADYGMATU OBIEKTOWEGO.

Pomijając fakt, że połowa wzorców musiałaby być "nieobiektowa", bo nie tylko strategia nie wymaga wstrzyknięcia niczego w kostruktor.

Tak.

omenomn2 napisał(a):

Dekorator jest za to pół obiektowy, bo obiekt dekorowany nie przyjmuje nic do konstruktora, ale dekoratory już tak.

Dekorator może przyjąć obiekt, który przyjmuje np int albo string, powiedzmy.

new CacheMemory(new Memory(1024)).

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