Pozbycie się setterozy i getterozy

0

Bawię się w pisanie immutowalnych klas, do tej pory udawało mi się nie mieć ani jednego geta i seta, ale teraz mam problem:

public final class IssueProducerImpl implements IssueProducer {

    private final static Logger logger = LoggerFactory.getLogger(IssueProducerImpl.class);

    private final Service service;
    private final IssueStatus wantedStatus;
    private final BlockingQueue<Issue> queue;
    
    @Inject
    public IssueProducerImpl(Service service, IssueStatus wantedStatus) {
        this.service = service;
        this.wantedStatus = wantedStatus;
    }

    @Override
    public void run () {
        while ( true ) {
            try {
                produce(wantedStatus).forEach(i -> queue.offer(i));
                Thread.sleep(30000);
            } catch ( InterruptedException e ) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public Iterable<Issue> produce( Status status) {
        return service.doSth(status);
    }

    @Override
    public void setQueue(BlockingQueue<Issue>(BlockingQueue<Issue> queue) {
        this.queue = queue;
    }
}

Użycie:

public final class PernIssueStatusChanger {

    private final IssueProducer producer;
    private final IssueConsumer consumer;

    @Inject
    public PernIssueStatusChanger(IssueProducer producer, IssueConsumer consumer, BlockinQueue<Issue> queue) {
        producer.setQueue(queue);
        consumer.setQueue(queue);
        this.producer = producer;
        this.consumer = consumer;
    }

    public void start() throws InterruptedException {
        new Thread(producer).start();
        new Thread(consumer).start();
    }
}

jak pozbyć się tego seta by obiekt był niemutowalny?
Z tego co mi przychodzi do głowy zaryzykowałbym najbardziej opakowanie tego w drugi obiekt

public final class IssueProducerCreatorImpl {

    private final Service service;
    private final IssueStatus wantedStatus;

    @Inject
    public IssueProducerCreatorImpl (Service service, IssueStatus wantedStatus) {
        this.service = service;
        this.wantedStatus = wantedStatus;
    }

    public IssueProducer getIssueProducer(BlockingQueue<Issue> queue) {
        return new IssueProducerImpl(service, wantedStatus, queue);
    }
}

i

public final class IssueProducerImpl implements IssueProducer {

    private final static Logger logger = LoggerFactory.getLogger(IssueProducerImpl.class);

    private final Service service;
    private final IssueStatus wantedStatus;
    private final BlockingQueue<Issue> queue;

// no injection!
// package scope
    IssueProducerImpl(IssueProducerConfiguration conf, BlockingQueue<Issue> queue) {
        this.service = conf.service;
        this.wantedStatus = conf.wantedStatus;
        this.queue = queue;
    }

    @Override
    public void run () {
        while ( true ) {
            try {
                produce(wantedStatus).forEach(i -> queue.offer(i));
                Thread.sleep(30000);
            } catch ( InterruptedException e ) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public Iterable<Issue> produce( Status status) {
        return service.doSth(status);
    }
}

Użycie:

public final class StatusChanger{

    private final IssueProducer producer;
    private final IssueConsumer consumer;

    @Inject
    public StatusChanger(IssueProducerCreatorImpl producerCreator, IssueConsumerCreatorImpl consumerCreator, BlockinQueue<Issue> queue) {
        this.producer = producerCreator.getIssueProducer(queue);
        this.consumer = consumerCreator.getIssueConsumer(queue);
    }

    public void start() throws InterruptedException {
        new Thread(producer).start();
        new Thread(consumer).start();
    }
}
5

Nie wiem co chcesz osiągnąć ale zrób to chociaż po bożemu.

Poza tym, Impl nigdy nie wygląda dobrze

1

Ja chyba nie rozumiem problemu. Czemu nie przekazujesz tej kolejki przy tworzeniu tych obiektów?

0
Shalom napisał(a):

Ja chyba nie rozumiem problemu. Czemu nie przekazujesz tej kolejki przy tworzeniu tych obiektów?

zeby miec lepsza kontrole i byc pewien ze Producer i Consumer współdzielą kolejkę, a poza tym musialbym chyba zrobic singleton beana kolejki.

2
Julian_ napisał(a):
Shalom napisał(a):

Ja chyba nie rozumiem problemu. Czemu nie przekazujesz tej kolejki przy tworzeniu tych obiektów?

zeby miec lepsza kontrole i byc pewien ze Producer i Consumer współdzielą kolejkę, a poza tym musialbym chyba zrobic singleton beana kolejki.

A kto ma wiedzę o tym, że Producer i Consumer mają współdzielić kolejkę? Kto tworzy Producera i Consumera? (Może to naturalny kandydate do tego by przekazać tę samą kolejką do Producera i Consumera?)

0
yarel napisał(a):
Julian_ napisał(a):
Shalom napisał(a):

Ja chyba nie rozumiem problemu. Czemu nie przekazujesz tej kolejki przy tworzeniu tych obiektów?

zeby miec lepsza kontrole i byc pewien ze Producer i Consumer współdzielą kolejkę, a poza tym musialbym chyba zrobic singleton beana kolejki.

A kto ma wiedzę o tym, że Producer i Consumer mają współdzielić kolejkę? Kto tworzy Producera i Consumera? (Może to naturalny kandydate do tego by przekazać tę samą kolejką do Producera i Consumera?)

ComponentAccessor - moja implementacja context beanów... mam problem ze zrobieniem singletona tam

0
Julian_ napisał(a):

A kto ma wiedzę o tym, że Producer i Consumer mają współdzielić kolejkę? Kto tworzy Producera i Consumera? (Może to naturalny kandydate do tego by przekazać tę samą kolejką do Producera i Consumera?)

ComponentAccessor - moja implementacja context beanów... mam problem ze zrobieniem singletona tam

Nawiązując do Twojego kodu, do dlaczego StatusChanger nie może tworzyć tej kolejki i przekazywać do Producera/Consumera? Tylko oczekuje tej kolejki w konstruktorze?
Czy ta kolejka musi być widoczna poza Producerem/Consumerem?

0

To czemu to jest w ogóle stan pruducera/consumera a nie argument metody run?

0
yarel napisał(a):
Julian_ napisał(a):

A kto ma wiedzę o tym, że Producer i Consumer mają współdzielić kolejkę? Kto tworzy Producera i Consumera? (Może to naturalny kandydate do tego by przekazać tę samą kolejką do Producera i Consumera?)

ComponentAccessor - moja implementacja context beanów... mam problem ze zrobieniem singletona tam

Nawiązując do Twojego kodu, do dlaczego StatusChanger nie może tworzyć tej kolejki i przekazywać do Producera/Consumera? Tylko oczekuje tej kolejki w konstruktorze?
Czy ta kolejka musi być widoczna poza Producerem/Consumerem?

chyba nie musi... ta kolejka to jest mój obiekt rozszerzajacy BlockingQueue tak by przyjmował tylko wartosci unikatowe.

Shalom napisał(a):

To czemu to jest w ogóle stan pruducera/consumera a nie argument metody run?

interfejs IssueProducer dziedziczy po Runnable

0

interfejs IssueProducer dziedziczy po Runnable

No i co? o_O Skoro logika nie pasuje do tegoż interfejsu, to niech przestanie go implementować.

Wszystko zależy od sytuacji. Ja jestem w stanie sobie wyobrazić taki przypadek, że masz jakiegoś bezstanowego producera któremu dajesz kolejkę i mówisz produkuj mi tutaj ;] Szczególnie że nie masz tam żadnego pośredniego stanu tego producera i można by jednego używać w wielu wątkach do wielu kolejek.

0

A może użyć anotacji @Getter oraz @Setter? Więcej tutaj (Lombok):

https://www.toptal.com/java/write-fat-free-java-code-project-lombok

0

Nie trzeba mnie przekonywać do zalet klas niezmienniczych (Immutable), ale to nie zrobiło się jakąś religią?
"Jakieś" klasy muszą mieć stan zmieniający się..

A tu (jeśli za mało kawy wypiłem, to wybaczcie) myślę od godziny, czy klasa mocno ożeniona z wątkami w swojej istocie jest niezmiennicza?

2

@AnyKtokolwiek to nie jest do końca prawda. Oczywistym jest że pewne obiekty muszą trzymać stan, ale wcale nie ma potrzeby mutowania tych obiektów. Zawsze da się zrobić nowy obiekt ;)

0
Shalom napisał(a):

@AnyKtokolwiek to nie jest do końca prawda. Oczywistym jest że pewne obiekty muszą trzymać stan, ale wcale nie ma potrzeby mutowania tych obiektów. Zawsze da się zrobić nowy obiekt ;)

A druga część wypowiedzi?
Czy klasa (nie posiadająca setterów) ale będąca właścicielem pliku, strumienia, socketa, wątku (obiekt charakteryzujący się życiem) jest immutabvle? Czy to tylko onanizowanie się?
Dobrze wiemy, że branża jest czuła na odruch stadny, było wstrzykuj, teraz jest nie wstrzykuj itd ... A gdzie zdrowy rozsądek.

Coś z tą definicją settery != immutable jest nie tak. Czułem to intuicyjne od dawna, a męka z tym wątkiem mi to pozwoliła werbalnie nazwać.

1
AnyKtokolwiek napisał(a):

Czy klasa (nie posiadająca setterów) ale będąca właścicielem pliku, strumienia, socketa, wątku (obiekt charakteryzujący się życiem) jest immutabvle? Czy to tylko onanizowanie się?
Dobrze wiemy, że branża jest czuła na odruch stadny, było wstrzykuj, teraz jest nie wstrzykuj itd ... A gdzie zdrowy rozsądek.

Coś z tą definicją settery != immutable jest nie tak. Czułem to intuicyjne od dawna, a męka z tym wątkiem mi to pozwoliła werbalnie nazwać.

Bo to jest pół definicji. Żeby mieć prawdziwe immutable i programowanie czysto funkcyjne musisz mieć jeszcze monadę IO na takie wypadki o których piszesz. Usunięcie seterów jest pierwszym kroniem, ale nie ostatnim. Jeśli chodzi o monadę IO to dla Scali jest biblioteka ZIO, dla Javy chyba jeszcze nic takiego nie powstało

9

Zawsze można zrobić 80% niemutowalnych i 20% mutowalnych bez żadnych monad ZIO i innych cudawianków :) tylko z głową :) masz encję, której stan musisz zmienić i nie kalkuluje Ci się robienie immutable? No trudno, jeśli umiesz to uzasadnić, to naprawdę Twoja kariera ani projekt się od tego nie zawali. Sytuacja nigdy nie jest 0-1, jest pełne spektrum rozwiązań, tylko trzeba wybrać.

1

Widzę to tak: jak chcesz mieć immutable, to musisz zadeklarować pola jako final, jak masz takie pole, to musisz ustawić jego wartość w konstruktorze.
Twoje IssueProducer i IssueConsumer powinny mieć zapodaną kolejkę w miejscu gdzie są tworzone i przestaje być potrzebne ustawianie ich setterem.

1
piotrpo napisał(a):

Widzę to tak: jak chcesz mieć immutable, to musisz zadeklarować pola jako final, jak masz takie pole, to musisz ustawić jego wartość w konstruktorze.
Twoje IssueProducer i IssueConsumer powinny mieć zapodaną kolejkę w miejscu gdzie są tworzone i przestaje być potrzebne ustawianie ich setterem.

class AmIMutable { final Mutable aField; AmIMutable(){ aField = ... ; } } Po wskazaniu mi w komentarzach problemów, kod będący treścią zagadki wygląda: ```java final class AmIMutable { private final Mutable aField;

AmIMutable(){
aField = ... ;
}
}

Główne pytanie zagadki brzmi: czy to jest klasa Immutable / mutable
Wg ciebie jest ... ?
0
Kamil Żabiński napisał(a):
AnyKtokolwiek napisał(a):

Coś z tą definicją settery != immutable jest nie tak. Czułem to intuicyjne od dawna, a męka z tym wątkiem mi to pozwoliła werbalnie nazwać.

Bo to jest pół definicji. Żeby mieć prawdziwe immutable i programowanie czysto funkcyjne musisz mieć jeszcze monadę IO na takie wypadki o których piszesz. Usunięcie seterów jest pierwszym krokiem, ale nie ostatnim. Jeśli chodzi o monadę IO to dla Scali jest biblioteka ZIO, dla Javy chyba jeszcze nic takiego nie powstało

Brakowało mi teorii do intuicji, dzięki.
EDIT: Na poziomie intelektualnym nieco podobny jest problem Serializable dla zawierających się obiektów, tylko są do tego słowa kluczowe (myślę o transient) i dedykowany formalizm. To wystarcza, abym zapalić migającą pomarańczową lampkę

Charles_Ray napisał(a):

Zawsze można zrobić 80% niemutowalnych i 20% mutowalnych bez żadnych monad ZIO i innych cudawianków :) tylko z głową :) masz encję, której stan musisz zmienić i nie kalkuluje Ci się robienie immutable? No trudno, jeśli umiesz to uzasadnić, to naprawdę Twoja kariera ani projekt się od tego nie zawali. Sytuacja nigdy nie jest 0-1, jest pełne spektrum rozwiązań, tylko trzeba wybrać.

Podoba mi się Twoja myśl. Właśnie o to chodzi, z użyciem głowy a nie IDE.
Religijne podejście posuwa ludzi do fałszowania kodu.

Ogólnie moje Złote Słówko Tygodnia to "pudrowanie". Generalnie można mieć gotowce do pudrowania gotowe nawyki w palcach albo w IDE. Tutaj upudrować mutable na immutable. W innym wątku w/s REST/HATEOAS odezwałem się, tam (tzn w branży, w tej dziedzinie) zachodzi pudrowanie stateful na stateles (i jeszcze inne), i tym upudrowanym "statelesem" się realizuje RPC (aby broń boże nie było widać, ze to RPC), często rozłożone w czasie (tzn nie atomowe). W konsekwencji ŻADEN sposób dokumentacji dla czystego REST nie jest adekwatny.

A pudrowanie mi się kojarzy z modą. Moda ważniejsza niż rozsądek, to jest baza do takich zachowań. Każda idea podczas jej umasawiania ulega spłyceniu, jak klarownie powiedział Kamil.

1
AnyKtokolwiek napisał(a):

Podoba mi się Twoja myśl. Właśnie o to chodzi, z użyciem głowy a nie IDE.
Religijne podejście posuwa ludzi do fałszowania kodu.

Nie nazywałbym tego religijnym podejściem, a dostosowaniem narzędzi do problemu.

Niemutowalność jest fajna, zalecana i tak dalej, ale Java wspiera programistę piszącego w takim stylu tak a nie inaczej - innymi słowy, praktycznie w ogóle. Dlatego niektóre problemy, które są trywialne w innych językach, mogą być bardzo skomplikowane w Javie (i vice versa). Ona nie była projektowana tak, aby wykorzystywać na każdym kroku takie podejście.

Inną kwestią jest to, że można napisać funkcję "czystą" oraz spełniającą referential transparency, a jednak korzystającą z mutowalnego stanu. Znowu, wszystko zależy od problemu i narzędzi jakimi dysponujemy.

Fajnie, że aktualnie panuje trend na programowanie funkcyjne i podbieranie różnych praktyk zdefiniowanych w nim do innych języków. Jednocześnie, trzeba sobie zdać sprawę, że czasami to nie ma sensu i sprawia więcej kłopotu niż daje korzyści (na przykład robienie z Javy upośledzonego języka funkcyjnego).

2
AnyKtokolwiek napisał(a):
final class AmIMutable {
   private final Mutable aField;

   AmIMutable(){
      aField = ... ;
   }
}

Główne pytanie zagadki brzmi: czy to jest klasa Immutable / mutable
Wg ciebie jest ... ?

Teraz troche już wchodzimy w obszary mistyczne i możliwe, że następnym problemem będzie ilość diabłów możliwa do umieszczenia na szpilce, ale spróbujmy.
Klasa nie może być immutable mutable. Takimi atrybutami może się cechować jej instancja (obiekt). Definicja klasy może niezmienność wykluczać (settery), ale w większości realnych przypadków nie jesteś w stanie pisząc kod pojedynczej, nietrywialnej klasy w kodzie produkcyjnym zapewnić, że stworzony obiekt będzie niemodyfikowalny.
W twoim przykładzie zapisałeś, że klasa zawiera private final Mutable aField, tylko możliwe jest w większości przypadków stworzenie niemutowalnej klasy dziedziczącej po mutowalnym rodzicu i odwrotnie.
Cała zabawa w tworzenie klas niezmiennych ma o ile wiem 2 cele:

  1. Szybsze wykonywanie kodu - na ogół aspekt poboczny z pomijalnie małymi, bądź nie istotnymi zyskami.
  2. Wyeliminowanie pułapki, gdzie wielu programistów robi sobie krzywdę, bo okazuje się, że tworzą się nienadzorowane przez nikogo powiązania w kodzie.

Wracając do twojego przykładu dopiszmy sobie tworzenie instancji tego obiektu w osobnej klasie:

public AmlMutable createamiMutable(){
  Mutable mutable = new Mutable();
  mutable.setSomething("something");
  return new AmlMutable(mutable);
}

Zakładając, że AmlMutable nie wystawi dostępu do swojego pola prywatnego, tak otrzymana struktura będzie niezmienną (z zewnątrz), bo nigdzie poza klasą docelowa nie jest przechowywany uchwyt do zmiennego obiektu. Wewnątrz AmiMutable można zmian dokonywać, ale taki problem jest dużo łatwiejszy do ogarnięcia przez programistę (bo zamyka się w pojedynczej klasie), a w przypadku gdy już do niego dojdzie łatwo go zdiagnozować i naprawić.
Dla odmiany kod potrafiący zrobić krzywdę:

private final Mutable mutable;
public AmlMutable createamiMutable(String mutator){
  mutable.setSomething(mutator);
  return new AmlMutable(mutable);
}

public Mutable getMutable(){
  return mutable;
}

I właśnie z każdego miejsca w systemie możemy zmienić private final field klasy Mutable. Tworząc nową instancję zmieniamy wszystkie już istniejące, a nawet nie wiemy ile ich jest.

0

Proszę bardzo naklepałem coś w stylu tego problemu o co mi chodziło.

https://github.com/julian4programmers/gownokod

Chętnie poznam uwagi.
Zastanawiam się czy zamiast pętli while w Consumer i Producer nie zrobić Timera?

1

Po co Ci tyle interface?
Dodatkowo ich nazwy niewiele mówią o tym co robią.
Poza tym jeszcze raz zapytam, dlaczego nie użyjesz po prostu ScheduledExecutorService?

Jakbyś potrzebował bibliotekę do przetestowania tego to zapraszam do mojej sygnaturki ( ͡° ͜ʖ ͡°)

1

Na temat tematu pisz w postach ;)
Scheduler jeden i dwa obiekty "zadań" które robiłyby swoją robotę. Ewentualnie ponieważ chcesz losowość to jeszcze gdzieś jakiś obiekt który do tego schedulera wrzucałby te taski z losowym czasem odpalenia

3

Ogólnie to kod wygląda przyjaźnie. Niestety nie mam czasu na jakiś review, ale pierwsze wrażenie naprawdę ok.

Z negatywów (sorry, ale nie bedę Ci pisał pozytywów, bo jeszcze staniesz się arogancki i pewny siebie).

nie podoba mi sie brak buildu. Co ja mam z tym zrobić?

To jest takie sobie:

private String parseNotNull(String str) {
        return Optional.of(str).orElseThrow(() -> new PropertyParameterNotFoundException(str));
    }

W standardzie masz https://docs.oracle.com/javase/8/docs/api/java/util/Objects.html#requireNonNull-T- lub assert str !=null
wyjdzie na to samo ( bo obsługi tego wyjątku swojego i tak nie masz).

No i gdzie są tosty?

0
jarekr000000 napisał(a):

nie podoba mi sie brak buildu. Co ja mam z tym zrobić?

musiałbym udostępnić jary klienta tej aplikacji do której on woła... dlatego poma też nie dałem i ocenzurowałem nazwę tej aplikacji jako xxx lub myapp.

No i gdzie są tosty?

Nie wiem co tu testować unitowo... musiałbym zamockować se tę aplikację myapp a taki test chyba nic nie wniesie... nie wiem też jeszcze jak testować wątki.

1
Julian_ napisał(a):

Nie wiem co tu testować unitowo... musiałbym zamockować se tę aplikację myapp a taki test chyba nic nie wniesie... nie wiem też jeszcze jak testować wątki.

Ja tez nie wiem, i nie wiem czemu unitowo, - normalnie testuj :-)
Btw. też czasem nie pisze testów, przeważnie jak nie wiem co robię (akurat w tej chwili mam taki dziwny projekt).

0

a zastanawiam się jak to wrzucić na serwer jakbym chciał np. żeby mi to się kreciło bez końca na Jbossie?
do jara to spakować?
czy zrobić WARa z RESTem, gdzie GET będzie odpalał to coś.

3

Jara. I odpalasz normalnie jak komendę.
Jar w dockerze, jako serwis na kubernetes to bardziej wypasiona opcja (ale ma sens jesli już masz taka infrastrukture (odpalasz na ktorejs chmurze)).

0
danek napisał(a):

Poza tym jeszcze raz zapytam, dlaczego nie użyjesz po prostu ScheduledExecutorService?

Tylko ze przerwy miedzy uruchomieniami sa losowe. Np. Drugie uruchomienie Producera moze wystapic po 20 sek. A trzecie po 1 sek. od drugiego.
Z tego co widze w tym Schedulerze opoznienia sa stale.

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