Kirjautuminen

Haku

Tehtävät

Keskustelu: Yleinen keskustelu: Muuttujan käpistely constructorissa

Sivun loppuun

runeberg [24.03.2013 00:47:39]

#

Tuli tässä mieleen, kun kirjoitin yhden epäonnistuneen vinkin toiseen ketjuun, että mikä mahtaa olla oikean kooditavan mukaista käsitellä luokan muuttujia rakentajassa?
Esimerkkiä:

public class Jaakiekko
{
  private bool tapparaVoittaaMestaruuden;

  public Jaakiekko Jaakiekko()
  {
    this.TapparaVoittaaMestaruuden = true;
  }

  public bool TapparaVoittaaMestaruuden
  {
    get { return this.tapparaVoittaaMestaruuden; }
    set { this.tapparaVoittaaMestaruuden = value; }
  }
}

Vai:

public class Jaakiekko
{
  private bool tapparaVoittaaMestaruuden;

  public Jaakiekko Jaakiekko()
  {
    this.tapparaVoittaaMestaruuden = true;
  }

  public bool TapparaVoittaaMestaruuden
  {
    get { return this.tapparaVoittaaMestaruuden; }
    set { this.tapparaVoittaaMestaruuden = value; }
  }
}

Niin, kumpaa pitäisi oikeaoppisesti kutsua constructorissa, private voi public? Oon ite aina käpistellyt publicia, kun periaatteessa saman "arvoisista" kyse, kun luodaan instanssi. En ole oikeastaan ikinä tarkistanut asiaa, vaikka monesti hämännyt. Periaatteessa ajaa saman asian, mutta miten "virallisesti" pitäisi?

Edit: Tämähän on kirjoitettu C#, mutta sama asia kaikissa kielissä missä rakentaja, getteri ja setteri.

punppis [24.03.2013 06:36:09]

#

Minun logiikkani sanoo, että jos kerran setteri on olemassa, niin sitten setterin kautta laitetaan datat sisään.

The Alchemist [24.03.2013 13:09:47]

#

Alkaa olla vähän vanhanaikaista puhua näistä jutuista, mutta kaipa sisäisesti suoraa membereiden käsittelyä - varsinkin konstruktorissa - voi perustella suorituskyvyllä. Itsekin kyllä pääsääntöisesti käytän get/set-funktioita myös olion muissa funkkareissa.

Metabolix [24.03.2013 14:29:40]

#

Erikoista on, että koodissasi ei ole yhtään rakentajaa vaan ainoastaan virheellisesti nimettyjä funktioita. ("`Jaakiekko.Jaakiekko()': member names cannot be the same as their enclosing type.")

Sinänsä kysymykseesi ei ole oikeaa vastausta, vaan varmasti joissain tilanteissa molemmille tavoille on perusteensa: joskus setteri tekee jotain tärkeää ja on lähes välttämätöntä käyttää sitä, ja joskus setteri tekee jotain sellaista, mitä alustuksessa ei haluta tapahtuvan. Kolmas (mielestäni usein paras) vaihtoehto on ilmoittaa alkuarvo jo jäsenen esittelyssä, jos se on aina sama.

qeijo [25.03.2013 11:42:06]

#

Sinänsä kysymykseesi ei ole oikeaa vastausta, mutta eikös lähtökohtaisesti kannattaisi aina valita se ratkaisu joka aiheuttaa vähiten kytkentää, eli minimoida koodin sisäisiä riippuvuuksia jopa luokan omien jäsenten välillä. Mutta ei tietenkään sillä kustannuksella että jäsenmuuttujat joutuisivat julkiseen skopeen.

Othnos [25.03.2013 16:59:22]

#

Jos tapaus on yhtä triviaali kuin kyseisessä esimerkissä niin miksi koko gettereitä ja settereitä tarvitaan ja miksi muuttuja ei voisi olla julkisessa näkyvyysalueessa? Jos taas setterillä olisi joku merkitys ja rakentajassa annettaisiin muuttujalle arvo parametrinä niin sillonhan on perusteltua ajaa kyseinen arvo setterin lävitse. Komppaan myös tuota Metabolixin "kolmatta vaihtoehtoa".

qeijo [26.03.2013 08:43:52]

#

Othnos kirjoitti:

ja miksi muuttuja ei voisi olla julkisessa näkyvyysalueessa?

Varmaankin ymmärrät mitä tarkoitan?. Toki muuttujia voi olla julkinen, kuitenkin "kapseloinnin" avulla, luokka on mm. helpompi ylläpitää.

Jouko Koski [26.03.2013 09:17:41]

#

Risu-C:ssä gettereiden ja settereiden tarkoitus on, että luokan sisäistäkin muuttujaa voidaan käsitellä kuin se olisi julkisessa näkyvyysalueessa. Jos kelvollisia arvoja ei ole rajattu, se voi olla ihan hyvin julkinen muuttuja. Kapselointi ei ole itsetarkoitus vaan keino.

Settereiden käyttö konstruktorissa voi olla sekavaa, jos setterit todella tekevät tarkistuksia. Jos arvon kelvollisuus vaikkapa riippuu toisesta muuttujasta, voi olla vaikeuksia varmistaa, ovatko muuttujat saaneet arvonsa settereiden olettamassa riippuvuusjärjestyksessä.

qeijo [26.03.2013 09:47:16]

#

Jouko Koski kirjoitti:

Jos kelvollisia arvoja ei ole rajattu, se voi olla ihan hyvin julkinen muuttuja.

Jos luokka sisältää julkisia muuttujia, niin et voi mennä muuttamaan sen sisäistä rakennetta tai tietotyyppiä, ilman pelkoa siitä että se rikkoo jossain jotain.

Vaikka ei alussa arvoja ei ole rajattu, niin luomalla julkisen muuttujan, varmistat että siinä periaatteessa myös pysytään.

Eli kapseloinnin sekä gettereiden ja settereiden avulla voidaan luoda yksi julinen kytkentäkohta luokan ja muun järjestelmän välillä, ja saadaan vapaasti älskätä ja muunnella luokan yksityistä rakennetta.

Jouko Koski [26.03.2013 12:26:22]

#

qeijo kirjoitti:

Jos luokka sisältää julkisia muuttujia, niin et voi mennä muuttamaan sen sisäistä rakennetta tai tietotyyppiä, ilman pelkoa siitä että se rikkoo jossain jotain.

Tilanne on käytännössä aivan sama, vaikka muuttuja olisikin getterin/setterin takana.

qeijo kirjoitti:

Vaikka ei alussa arvoja ei ole rajattu, niin luomalla julkisen muuttujan, varmistat että siinä periaatteessa myös pysytään.

Esim. C#:ssä näin ei ole, koska getterit/setterit näyttävät viittauksilta julkiseen muuttujaan.

Grez [26.03.2013 12:52:15]

#

Jouko Koski kirjoitti:

Esim. C#:ssä näin ei ole, koska getterit/setterit näyttävät viittauksilta julkiseen muuttujaan.

Eivät näytä. Kuva (Tai ainakin minä erotan sinisen laatikon sormesta joka osoittaa ominaisuuslistaa.)

Ne eivät myöskään toimi samalla tavalla, kuten tämän kuvan virhealleviivauksistakin huomaa.

qeijo [26.03.2013 12:53:43]

#

Jouko Koski kirjoitti:

qeijo kirjoitti:

Jos luokka sisältää julkisia muuttujia, niin et voi mennä muuttamaan sen sisäistä rakennetta tai tietotyyppiä, ilman pelkoa siitä että se rikkoo jossain jotain.

Tilanne on käytännössä aivan sama, vaikka muuttuja olisikin getterin/setterin takana.

Ei ole, kyllä ominaisuudet tarjoavat paremmat mahdollisuudet tehdä rakenteellisia muutoksia luokan sisäisesti säilyttäen julkisen liittymän ennallaan.

Jouko Koski kirjoitti:

qeijo kirjoitti:

Vaikka ei alussa arvoja ei ole rajattu, niin luomalla julkisen muuttujan, varmistat että siinä periaatteessa myös pysytään.

Esim. C#:ssä näin ei ole, koska getterit/setterit näyttävät viittauksilta julkiseen muuttujaan.

Tarkoitatko että tekisit jälkikäteen ominaisuuden aikaisemman julkisen muuttujan nimellä, jotta ketju ei katkeaisi? En ymmärrä.

Jouko Koski [26.03.2013 14:33:01]

#

Jos luokan sisäisen muuttujan tyyppi vaihtuu, muutos on lähes aina sellainen, että get/set-tyypit muuttuvat myös.

qeijo kirjoitti:

Tarkoitatko että tekisit jälkikäteen ominaisuuden aikaisemman julkisen muuttujan nimellä, jotta ketju ei katkeaisi?

Täsmälleen.

Grez [26.03.2013 14:39:21]

#

Sinänsähän muuttujan vaihtaminen ominaisuudeksi (nimen säilyttäen) saattaa rikkoa vaikka mitä. Eli sikäli minusta ei voi puhua että "ketju ei katkeaisi".

Toki hyvällä tuurilla voi käydä niin että se onnistuu suoraan.

Jouko Koski [26.03.2013 16:05:12]

#

Grez kirjoitti:

Sinänsähän muuttujan vaihtaminen ominaisuudeksi (nimen säilyttäen) saattaa rikkoa vaikka mitä.

Kuten? Käyttörajapinta pysyy samana, joten kyse on jostain muusta.

Viittausten (ref) käyttö on kyllä erikoinen poikkeustapaus muutenkin. Sen kuuluukin mennä rikki, sillä se sivuuttaa kelpoisuustarkitukset, joita luokka ryhtyy käyttämään.

Grez [26.03.2013 16:45:57]

#

Mitä tarkoitat "käyttörajapinnalla"? Olion rajapintahan muuttuu aika radikaalisti, kun muuttuja X poistuu ja tilalle tulee ominaisuus X.

Jos muutat jostain kirjastostasi muuttujan ominaisuudeksi, niin joudut sen jälkeen kääntämään kaikki kirjastoa käyttävät ohjelmat uudestaan.

Ja noi out ja ref oli vaan hyvin pintaraapaisuesimerkki siitä mikä voi mennä rikki jos muuttelee muuttujia ominaisuuksiksi tai päinvastoin.

Jouko Koski [26.03.2013 20:11:05]

#

Ominaisuuden käyttäminen, siis käyttörajapinta, näyttää samalta kuin muuttujan. Sama se ei ole, sillä viittauskäytössä on tosiaan merkittävä ero.

Käytännössä muuttujien korvaamista ominaisuuksilla ei tapahdu usein. Vielä harvemmin sellaisten muuttujien, joita käytetään reffeinä tai outteina. Eihän kääntäjä sitä sallikaan, mutta niin pitkälle ei yleensä edes mennä, koska altistus viittauskäytölle on ollut ajatuksena jo luokkaa alun perin toteutettaessa. Valtaosa jäsenmuuttujien käyttöä on olla osapuolena jonkinlaisessa sijoituslausekkeessa, ei viittauksessa.

Jos muuttuja vaihdetaan ominaisuudeksi kirjastossa, silloinkaan sitä käyttävää ohjelmaa ei pitäisi joutua muuttamaan. Yleensä melko ripeähkösti tapahtuva uudelleenkääntäminen riittää.

Grez [26.03.2013 20:28:36]

#

Jouko Koski kirjoitti:

Ominaisuuden käyttäminen, siis käyttörajapinta, näyttää samalta kuin muuttujan. Sama se ei ole, sillä viittauskäytössä on tosiaan merkittävä ero.

Siis ominaisuuden käyttämiskoodi on tietyissä erikoistapauksissa (arvon lukeminen ja arvon sijoittaminen) samanlaista kuin muuttujalla, mutta muuta yhteistähän noilla ei olekaan.

Jouko Koski kirjoitti:

Jos muuttuja vaihdetaan ominaisuudeksi kirjastossa, silloinkaan sitä käyttävää ohjelmaa ei pitäisi joutua muuttamaan. Yleensä melko ripeähkösti tapahtuva uudelleenkääntäminen riittää.

"Ei pitäisi joutua muuttamaan"? Siis tarkoitat kai, että "saattaa käydä pahuksen hyvä tuuri, eikä joudu muuttamaan". Kyseessähän ei ole mikään kielen ominaisuus vaan yksinkertaisesti hyvä tuuri. Voihan se olla että voit vaikka poistaa kirjastosta parit muuttujat ja funktiot, ja sitä käyttävät ohjelmat kääntyvät kivasti edelleen.

Noi ref ja out nyt oli vaan yksinkertaisimmasta päästä esimerkkejä. Jos ajatellaan vaikka ORMeja niin ne tyypillisesti käsittelee ominaisuudet mutta ei muuttujia.

Aika paljon voi "räjähtää" jos vaihdat muuttujan ominaisuukdeksi. Vielä enemmän voi räjähtää jos vaihtaa toisin päin. Ominaisuus voidaan esimerkiksi ylikirjoittaa perivässä luokassa, muuttujaa ei.

groovyb [26.03.2013 21:00:38]

#

Tosiaan, se että pitääkö käyttävää ohjelmaa muuttaa, kun muuttuja muutetaan ominaisuudeksi (ja päinvastoin), riippuu aika pitkälti itse ohjelmasta.
Ei voi sanoa että voi muuttaa, eikä että ei voi muuttaa. Eiköhän se softa määrää mitä sille voi tehdä.

Itse alkuperäiselle vastaajalle sanoisin, että itse pistäisin kamat sisälle setterin kautta, jos sellainen on kerran luotu. Jos annettu oletusarvo on vakio kuten esimerkissäsi, antaisin sen jo esittelyssä.

qeijo [27.03.2013 09:09:56]

#

Jouko Koski kirjoitti:

Jos muuttuja vaihdetaan ominaisuudeksi kirjastossa, silloinkaan sitä käyttävää ohjelmaa ei pitäisi joutua muuttamaan.

groovyb kirjoitti:

Tosiaan, se että pitääkö käyttävää ohjelmaa muuttaa, kun muuttuja muutetaan ominaisuudeksi (ja päinvastoin), riippuu aika pitkälti itse ohjelmasta.
Ei voi sanoa että voi muuttaa, eikä että ei voi muuttaa. Eiköhän se softa määrää mitä sille voi tehdä.

Mutta onhan se järjen köyhyyttä ja ennen kaikkea huono ohjelmointitapa jos näin joutuu tekemään.

groovyb [27.03.2013 10:49:40]

#

Niin on.

tepokas [27.03.2013 11:12:08]

#

Ketjun alottajalle sen verran neuvoa, että ensimmäisenä tulisi kiinnittää huomiota koodin/muuttujien nimeämislogiikkaan.

Esim. muuttuja TapparaVoittaaMestaruuden ei vastaa todellisuutta ja se tuleekin nimetä muotoon JypVoittaaMestaruuden.

Näin saadaan koodin logiikka vastaamaan todellisuutta eikä public/private-käytöllä ole merkitystä.

JaskaP [27.03.2013 21:52:59]

#

Tämäkin keskustelu on 'edustava' esimerkki, kuinka olio-ohjelmointi on liian abstraktia jopa sitä osaaville.

Sekavaa lässytystä (jota en edes jaksa lukea), ettekö te osaa edes selittää selkeästi kuinka sitä setter/getter-rojua pitää käyttää?

Itse luulisin osaavani selittää selkeästi, mutta kun olennaista on tehdä niitä varsinaisia metodeja *ja* välttää set/get-kamaa viimeiseen asti, niin jätän väliin.

(Kysyjän 'class Jaakiekko' on jopa esimerkkinä niin sontaa, ettei järkeviä funktioita/metodeja voi olla, ml. get/set)

Grez [27.03.2013 22:01:39]

#

JaskaP kirjoitti:

Itse luulisin osaavani selittää selkeästi, mutta kun olennaista on tehdä niitä varsinaisia metodeja *ja* välttää set/get-kamaa viimeiseen asti, niin jätän väliin.

No mielestäni tuossa on aika olennaista että mikä sen on olion käyttötarkoitus on. Monissa suosituissa kirjastoissa ja ohjelmointimalleissa käytetään tietovarasto-objekteja, jolloin tuntuisi aika turhalta korvata get/set kamat varsinaisilla metodeilla.

Vai oliko tarkoituksena sanoa, että esim. Hibernate-tyyppiset ORMit tai MVC-mallit on saatanasta.

JaskaP [27.03.2013 22:05:46]

#

Grez kirjoitti:

No mielestäni tuossa on aika olennaista että mikä sen on olion käyttötarkoitus on. Ellei tarkoituksena sitten ole sanoa että esim. Hibernate-tyyppiset ORMit tai MVC-mallit on saatanasta.

No, tuohan oli tosi selkeää.

Grez [27.03.2013 22:08:43]

#

JaskaP kirjoitti:

No, tuohan oli tosi selkeää.

Lavensin vähän viestiäni. Onko nyt parempi?

JaskaP [27.03.2013 22:10:06]

#

Grez kirjoitti:

Vai oliko tarkoituksena sanoa, että esim. Hibernate-tyyppiset ORMit tai MVC-mallit on saatanasta.

Ei ollut.

runeberg [28.03.2013 12:47:51]

#

Esimerkkini on huonosti toteutettu ja äärimmäisen pelkistetty, eikä sen ollut alunperinkään tarkoitus toimiakaan, sillä ajattelin että siitä huolimatta ihmiset saa siitä ajatuksesta kopin mitä haen. Nyt kyllä huomaan että esimerkki oli todella huono ja ominaisuudenkin olin nimenny muuttujaksi. Mutta valitettavasti en pystynyt keskiyön jälkeen parempaan, illan itkettyäni tuoppiin ja siitä päätyen pohtimaan elämän syvimpiä, mm. kyseistä asiaa. Anteeksi.

Mutta asia tuli nyt melko selkeeksi.

Edit. Pohdintani oli silloin varmaan liian korkealentoista, enkä osannut pukea sitä tekstiksi. Nyt jälkikäteen ajateltuna ongelmani oli varsin minimaalinen :)


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta