Kiedy używać properties a kiedy metod

1

Do tej pory miałem prosty pogląd na ten temat i używałem do pisania properties wyłącznie, żeby zastąpić gettery i settery (czyli wynik zmowy twórców Java i producentów klawiatur). Dla przykładu:

public class Rectangle{
  public Rectangle(int length, int height){
    ...
  
  }
  private final int length;
  private final int height;

  public int getLength()...
  public int getHeight()...

  public area(){
    return length * height;
  }
}

W takim Kotlinie zapisałbym tak:

class Rectangle(val length:Int, val height:height){
  fun area() length*height
}

Coś mnie podkusiło, żeby w końcu dać szansę ortodoksyjnemu TDD i zacząłem pisać tak teścik:

assertEquals(6, Rectangle(2, 3).area)

Bo skoro zaczynam od testu, to nie mam zielonego pojęcia, czy area jest propertisem, czy metodą, a nawet nie chcę tego wiedzieć, Chcę coś z obiektu odczytać, nie mam zamiaru go w żaden sposób zmienić tym odczytem, nie mam najmniejszej nawet potrzeby przekazania podczas tego odczytu jakiegokolwiek parametru, to po co metoda?
Wyszło coś takiego:

class Rectangle(val length:Int, val height:height){
  val area
    get() = length*height
}

I teraz troche nie wiem, czy to podejście jest OK. Wiadomo, jest jakaś fiksacja na Java i coś albo powinno być get'em i nie mieć logiki, albo mieć logikę i nie być get'em.

2

Może nie natemat, ale problem nie dotyczy Scali XD W Scali byś napisał:

class Rectangle(val length: Int, val height: Int){
  def area = length*height
}

Czyli nie ma rozróznienia medzy getterem a metodą bez parametrów. Co prawda można jeszcze napisać def area() = length*height, ale linter mi podpowiada że to bez sensu XD

0

No właśnie to taki moment, kiedy mój zadżawowany mózg doznał szoku poznawczego. Pomiędzy tym co jest w Scala a tym co napisałem wyżej różnica wydaje się być symboliczna. Fajnie wiedzieć, że linter ma podobne zdanie :) Jedyne ryzyko, że może to kogoś piszącego w java nieco zdziwić, bo tam będzie widoczne public int getArea()

0
piotrpo napisał(a):

Jedyne ryzyko, że może to kogoś piszącego w java nieco zdziwić, bo tam będzie widoczne public int getArea()

No to w Scali takiego lukru nie dostaniesz (ale pisze to z pewną niepewnością bo moja wiedza o łaczeniu Scali z Javą jest sprzed 4 lat). Nawet dla propertisów w Scali nie są generowane przedrostki get/set. Żeby były trzeba było dodać adnotację BeanProperty. Bez tego dla kodu Scalowego:

class Foo {
  var foo: Int = _
}

jest generowany taki odpowiednik kodu javowego

public class Foo {
  private int foo;
  public int foo();
  public void foo_$eq(int);
  public Foo();
}

Nie sprawdzałem a przeczytałem na SO

0

Jak nie ma parametrow ani side-effectow no to chyba properties.
PS: A czemu nie po prostu val area = width * height? :)

0

@stivens: Nie chcę nakłamać, ale to się na 99% nie liczy podczas tworzenia obiektu. Nie ma tam pola pod spodem.

0

No to lazy val area = width * height XD Chyba że w Kotlinie nie ma lazy?

Update to będzie po kotlinowemu:

val area: Int by lazy {
  width * height
}

?

0

get() chyba nie ma pola pod spodem. Ale czemu nie chcesz tego miec jako pola? Jak nie chcesz tego liczyc podczas tworzenia obiektu no to by lazy. Bo zakladamy ze width i height i tak sa niezmiennne. To po co Ci ten getter?

0

Jest lazy, faktycznie w tym przykładzie ma ono sens, pisałem coś trochę bardziej skomplikowanego i chciałem uprościć przykład dla lepszego rozumienia ;) Tak, w przypadku immutable object prostokąt i pobierania zawsze takiego samego wyniku ma sens jego przechowywanie.

0

I teraz troche nie wiem, czy to podejście jest OK. Wiadomo, jest jakaś fiksacja na Java i coś albo powinno być get'em i nie mieć logiki, albo mieć logikę i nie być get'em.

No ale gdzie tu logika? Po prostu czysta funkcja wykonująca banalne przeliczenie. Problem by był, gdybyś tam odpalał skutki uboczne albo dokonywał jakichś skomplikowanych wyliczeń (tak skomplikowanych, że miałyby wpływ na wydajność. Wtedy ktoś mógłby mieć WTF, jeśli mu się zamula cała apka, a po debugowaniu okazuje się, że winę ponosi dostęp do jakiejś właściwości, gdzie wydawałoby się, że nie ma logiki).
BTW to też ciekawy przykład na to, że nawet czyste funkcje mogą odpalać skutek uboczny w postaci zamulenia kompa, jeśli są tak wymagające obliczeniowo

W JS są niby właściwości z getterami/setterami, ale ja za nimi nie przepadam. Zaciemniają kod + z tego co mierzyłem, nie są wydajne (a bawię się w robienie gier, więc zwracam uwagę na tego typu rzeczy). Gdybym miał to napisać w JS, to prędzej zrobiłbym metodę area i wywoływał:

rectangle.area()

chociaż zrozumiałbym, jeśli inny programista JSa zrobiłby to na właściwościach:

rectangle.area

get() chyba nie ma pola pod spodem. Ale czemu nie chcesz tego miec jako pola? Jak nie chcesz tego liczyc podczas tworzenia obiektu no to by lazy. Bo zakladamy ze width i height i tak sa niezmiennne. To po co Ci ten getter?

liczenie dynamiczne ma tę zaletę, że nawet jak width/height się zmienią (zakładając mutowalność klasy), to area zawsze będzie świeże. A bez tego przy zmianie width/height należałoby uaktualnić area.

Chyba, że to ma być niemutowalne, to wtedy w JS nie robiłbym żadnej klasy z tego, tylko coś takiego:

const createRectangle = (width, height) => ({width, height, area: width * height});
const rect = createRectangle(10, 5);
console.log(rect.width, rect.height, rect.area);

i traktowałbym ten obiekt tak, jakby był niemutowalny.

1

liczenie dynamiczne ma tę zaletę, że nawet jak width/height się zmienią (zakładając mutowalność klasy), to area zawsze będzie świeże. A bez tego przy zmianie width/height należałoby uaktualnić area.

Ale tam jest val. To sie nie ma prawa zmienic. Chyba ze ktos recznie w pamieci bedzie mieszal :) A jakby sie mialo zmieniac (var) no to pisalem wczesniej

Jak nie ma parametrow ani side-effectow no to chyba properties.

1

Co do lazy... to trzeba jednak pamiętać, że to spowoduje powstanie pola, które jeszcze do tego będzie miało dość skomplikowaną procedure pobierania, inicjalizaji (synchronied standardowo)

Co do pytania. Tak, czasem różnica między "polami", a metodami zanika. W kotlinie można robić override na polach (i mocno z tego korzystam (np. w DI)).
val może być funkcją

   val calculate = {x:Int -> x*2}

funkcja może być "wartością"

   fun size() = 2

W Scali miałem taki jeden projekt, że funkcje były definiowane przez val, a pola przez def :-) niespecjalnie - samo wyszło. Dodaj do tego brak nawiasów w Scali przy wywołaniu funkcji bezargumentowych i jest ciekawie.

0

Przyglądam się waszej dyskusji i kompletnie nie mam pojęcia o czym gadacie, ta java to jakiś pokręcony język.

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