Tämmöistä perusjuttua tällä kertaa, jopa lähes vilosoofisia mittasuhteita lähestyy.
Kuvitellaan että meillä on luokka A ja luokka B. Luokasta A luodaan ilmentymä. Sen jälkeen olio A luo ilmentymän luokasta B. Luokka A voisi olla esim. jokin perus emoluokka, joka toimii alustana web-sovellukselle, sovitaan tässä, että sen tärkein tehtävä on hankkia vaikkapa sovelluksen asetukset. Nythän on niin, että olio B tarvitsee kyseisistä asetuksista jotain, mutta koska se on "samalla tasolla" kuin A::asetukset, se ei pääse niihin käsiksi. Mikä on fiksuin tapa ratkaista tämä?
Globalisointi?
Staattinen metodi? Jos data luetaan tiedostosta, luetaanko se monta kertaa jos samaa dataa käyttäviä ilmentymiä on monta?
A:n välittäminen B:lle viittauksena?
Muita tapoja?
Olen kokenut PHP-koodari, mutta olen lähestynyt PHP:tä vuosien varrella niin käytäntöorientoituneesti, että monet perusjutut, kuten tämä, ovat jääneet kakkosrooliin. Nyt tutkiskelen niitä hupiprojektin muodossa.
Tuntuu aika erikoiselta, että jos luokan A tehtävä on hankkia sovelluksen asetukset, niin se loisi luokan B, joka tarvitsee asetuksia.
Jos teet staattisen metodin, niin voit tehdä sen joko niin että se lukee ne joka kutsukerralla tai niin, että ne luetaan vain kerran ja myöhemmillä kerroilla palautetaan muistista. Tosin PHP-skriptithän ei tyypillisesti säily muistissa sivulatausten välillä, joten hirveän suurta merkitystä tuolla ei edes ole.
Lähdetään siitä, että luokka A tarvitsee niitä itsekin. Vähän koodiesimerkkiä:
class Website { var asetukset = $this->lataaAsetukset(); var sivu; function lataaAsetukset() { //ladataan... } function millaSivullaOllaan() { return new Page(/*pitkällisen selvityksen tulos*/); } }
Sovitaan, että Page-olio tarvitsee asetuksia jossain kohtaa, mutta samoin tarvitsee Website.
Grez kirjoitti:
Jos teet staattisen metodin, niin voit tehdä sen joko niin että se lukee ne joka kutsukerralla tai niin, että ne luetaan vain kerran ja myöhemmillä kerroilla palautetaan muistista. Tosin PHP-skriptithän ei tyypillisesti säily muistissa sivulatausten välillä, joten hirveän suurta merkitystä tuolla ei edes ole.
Selvennä vähän mitä tarkoitat muistilla. Ajattelin ensin, että puhut sessiosta, mutta et varmaankaan sitä tarkoita, koska sessiolla nimenomaan saa datan pysymään muistissa. Yritän tässä välttää sitä, että tiedostoista tai tietokannasta luetaan yhtään useampaa kertaa kuin on pakko.
Minulla on esimerkiksi staattinen PDO-wrapperiluokka (suoraan php.netin kommenteista), jonka avulla käytetään PDO:ta tyyliin DB::prepare('Blabla') jne. Mietin senkin kohdalla, että kun se kerran lataa tekstitiedostosta tietokannan käyttäjätunnarit, niin tekeekö se sen aina uudelleen joka kerta kun kutsutaan staattista metodia, vai onko se välimuistitettu jotenkin?
Kyseessä on tämä (voi olla täältäkin peräisin, mutta varmaksi en nyt muista):
class DB { private static $objInstance; private function __construct() {} private function __clone() {} public static function getInstance( ) { if(!self::$objInstance){ $dbConfig = load_config('db'); self::$objInstance = new PDO("mysql:host={$dbConfig->host};dbname={$dbConfig->database}", $dbConfig->username, $dbConfig->password); self::$objInstance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); self::$objInstance->exec("set names '{$dbConfig->charset}'"); } return self::$objInstance; } final public static function __callStatic( $chrMethod, $arrArguments ) { $objInstance = self::getInstance(); return call_user_func_array(array($objInstance, $chrMethod), $arrArguments); } public static function prepare($mysql) { $objInstance = self::getInstance(); return $objInstance->prepare($mysql); } }
Minulla on sellainen käsitys, että välimuistitus tässä tapauksessa tapahtuu rivillä 8. Olenko oikeassa?
Triskal kirjoitti:
Minulla on sellainen käsitys, että välimuistitus tässä tapauksessa tapahtuu rivillä 8. Olenko oikeassa?
Niin, toihan nyt tietysti on vain tietokantayhteyden pitäminen tallessa. Ihan yhtä lailla voisit laittaa muitakin staattisia muuttujia.
Triskal kirjoitti:
Selvennä vähän mitä tarkoitat muistilla.
Lähinnä sitä, missä tuossa esimerkissäsikin säilytetään $objInstance
Eli siis PHP:n muuttujathan säilyy muistissa sen aikaa kun skriptiä suoritetaan. Jokaisella sivunlatauksella skripti suoritetaan uudelleen ja siten myös nuo tehdään uudelleen.
Tietokantaolio ei tarvitse mitään globaalia conffia eikä sen tarvitse ainakaan käpistellä sitä läpi suorituksen, joten parempi antaa tarvitut tiedot ennen yhteyden avaamista ihan vain arvoina. Jotain tällaista olisin itse käyttänyt:
<?php class Database { private static $connection; private $pdo; static function connection() { if (!self::$connection) { self::$connection = new Database(); } return self::$connection; } function __construct() { // Tyhjä } function __call($method, $params) { if (!$this->isConnected()) { return false; } return call_user_func_array(array($this->pdo, $method), $params); } function connect($username, $password, $database, $server='localhost') { try { $this->pdo = new PDO(...); } catch (PDOException $e) { throw new Exception("Database connection error"); } } function isConnected() { return !is_null($this->pdo); } } // ...järjestelmän alustuksessa... $db = Database::connection(); if (!$db->isConnected()) { $username = $config->get('database_username'); $password = $config->get('database_password'); $database = ... $db->connect($username, $password, $database); }
Jos softa rupeaa menemään sellaiseksi, että useassa paikassa tarvitsee olevinaan lukea järjestelmän asetuksia, niin silloin aloittaisin projektin alusta, koska entinen koodi ei johda mihinkään mukavaan lopputulemaan.
Ja tämähän on nimenomaan käytäntöä ja käytännöllisyyttä aivan kaiken ytimessä, ei mitään filosofista hömppää.
Triskal kirjoitti:
Nythän on niin, että olio B tarvitsee kyseisistä asetuksista jotain, mutta koska se on "samalla tasolla" kuin A::asetukset, se ei pääse niihin käsiksi. Mikä on fiksuin tapa ratkaista tämä?
Tarvitseeko sen jostakin syystä olla samalla tasolla? Toisaalta hommaa helpottaisi abstracti,'action' ja 'model'-luokka , jotka toimii moottorina kaikille. Sen jälkeen jokainen luokka, joka perityy tuosta abstractista luokasta, voisi käyttää ihan mitä tahansa muuta luokkaa jotka kulkee saman moottorin kautta, pelkällä $this- viittauksella.
Ps. Tai ei ihan pelkällä $this-viittauksella vaan:
// ladataan vaikka viereiseesä hakemistosa oleva luokka: // hakemistossa model/hakemisto/filename.php $this->load->model('hakemisto/filename'); // ladataan kyseisen luokan meodi $this->model_hakemisto_filename->getMethod();
Tällaisessa tapauksessa filename.php tiedostossa (samoin kuin kaikissa muissakin paitsi 'moottorissa') olevan luokan nimessä täytyy tulla tuo luokan tiedostopolku esille, esim . "class ModelHakemistoFilename".
The Alchemist kirjoitti:
Jos softa rupeaa menemään sellaiseksi, että useassa paikassa tarvitsee olevinaan lukea järjestelmän asetuksia, niin silloin aloittaisin projektin alusta, koska entinen koodi ei johda mihinkään mukavaan lopputulemaan.
Sitähän tässä yritän just välttää. Tuo järjestelmän asetusten lukeminen on sitä paitsi vain yksi esimerkki, ei siihen nyt kannata takertua.
Pistemies kirjoitti:
Tarvitseeko sen jostakin syystä olla samalla tasolla?
Lähtökohtaisesti kyllä. Kyse on siitä, että yritän mallintaa www-sivustoa itselleni loogiseksi oliomalliksi. On www-sivusto. Www-sivuston sisällä on sivu. Sivun sisällä on sisältöelementti. Sisältöelementin sisällä on sisältöä. But wait, there's more.
Www-sivuston sisällä on myös tietokantayhteys, pyynnönkäsittelijä, autentikointi jne... Näiden kaikkien pitäisi olla ainoastaan kertaalleen alustettuja, mutta käytettävissä tarpeen mukaan myös Sivulle, Sisältöelementille jne.
Pistemies kirjoitti:
Toisaalta hommaa helpottaisi abstracti,'action' ja 'model'-luokka , jotka toimii moottorina kaikille. Sen jälkeen jokainen luokka, joka perityy tuosta abstractista luokasta, voisi käyttää ihan mitä tahansa muuta luokkaa jotka kulkee saman moottorin kautta, pelkällä $this- viittauksella.
Mulle on aina ollut epäselvää (ja on edelleen, vaikka kävin taas lukaisemassa abstraktiota koskevan luvun php.netissä) että miksi missään muussa tilanteessa kuin sellaisessa, jossa jonkun muun on tarkoitus laajentaa luomaani codebasea ja näin ollen vaaditaan idioottivarma rautalankamalli sille mitä luokassa pitää vähintään olla, hyödytään siitä että määritellään luokka abstraktiksi. Ja sitten toisaalta, oman käsitykseni mukaan interface olisi se rakenne joka on tarkoitettu nimenomaan siihen. Onko siihen erityistä painavaa ja perusteltua syytä miksi luokan pitäisi olla abstrakti?
Sinällään ajatus siitä, että olisi luokka, abstrakti tai ei, jossa olisi sovelluksen essentiaaliset toiminnallisuudet, jotka olisivat käytettävissä perivillä instansseilla, ei ole minulle vieras, mutta kysymys kuuluukin, että jos tällaisella framework-luokalla olisi vaikkapa säilössä tietokantayhteys ja metodi sen luomiselle, niin miten ehkäistään se, ettei jokainen perivä instanssi luo omaa instanssiaan siitä?
EDIT: Joku kävi näköjään muuttamassa aiheen otsikon.
Triskal kirjoitti:
Pistemies kirjoitti:
Toisaalta hommaa helpottaisi abstracti,'action' ja 'model'-luokka , jotka toimii moottorina kaikille.
idioottivarma rautalankamalli sille mitä luokassa pitää vähintään olla, hyödytään siitä että määritellään luokka abstraktiksi. Ja sitten toisaalta, oman käsitykseni mukaan interface olisi se rakenne joka on tarkoitettu nimenomaan siihen.
Miten interface voi toimia moottorina millekään, kun se ei sisällä toteutusta?
Abstraktin luokan idea on, että se voi toteuttaa asioita ja muut luokat voi periä sen. Luokan abstraktius vain kertoo, että siitä itsestään ei ole tarkoitus tehdä olioita.
Mä en oikeastaan edes täysin tajua mikä tässä on ongelmana. Kyllähän kaksi luokkaa voi olla toisistaan riippuvaisia. Sen sijaan et voi tehdä kahta funktiota jotka molemmat joka kerralla kutsuu toistaan (koska siitä seuraa stack overflow).
Tuohon on myös toinen vaihtoehto, johon en kuitenkaan ole itse käytännössä tutustunut. OsCommerce verkkokauppa käyttää tuohon tiedostojen hakemistopolun tunistukseen namespace -funktiota.
Noihin muuten pääsee tutustumaan 'vetämällä' verkosta muutaman erilaisen ilmaisen verkkokauppa-sovelluksen, jotka perustuu php:n vapaaseen lähdekoodiin. Niistä selviää paljon kun hiukan tutkii koodia.
Mutta näin pienessä jutussa kun on vain kaksi luokkaa, ei luulisi olevan ongelmia. Eikö pelkkä B:n perityminen A:sta riitä?
Grez kirjoitti:
Abstraktin luokan idea on, että se voi toteuttaa asioita ja muut luokat voi periä sen. Luokan abstraktius vain kertoo, että siitä itsestään ei ole tarkoitus tehdä olioita.
Noissa isoissa ohjelmissa abstraktia luokkaa on sovellettu siten, että se lataa esim. 20 php-luokkaa itseensä ja jokainen joka abstraktin luokan perii, voi sitten sieltä "periä" haluamansa luokan tai luokat $this->load -metodilla.
Grez kirjoitti:
Abstraktin luokan idea on, että se voi toteuttaa asioita ja muut luokat voi periä sen. Luokan abstraktius vain kertoo, että siitä itsestään ei ole tarkoitus tehdä olioita.
Tämä on ihan selvä juttu minulle. Yritin kuitenkin kalastella vielä vähän kouriintuntuvampaa perustelua sille miksi luokan pitäisi olla abstrakti. Ilmiselvästi se ei sitä vaadi, koska myös ei-abstraktia luokaa voi käyttää moottorina ihan samalla tapaa. Abstraktista luokasta ei voi luoda ilmentymää, ei-abstraktista luokasta sen voi muuten vaan jättää luomatta. Konkreettinen hyöty siitä että määrittelee luokan abstraktiksi on minulle edelleen epäselvä.
pistemies kirjoitti:
Mutta näin pienessä jutussa kun on vain kaksi luokkaa, ei luulisi olevan ongelmia. Eikö pelkkä B:n perityminen A:sta riitä?
Ei ole kahta luokkaa tai viittä luokkaa, vaan on Luokka A, joka toimii containerina datalle X, Y ja Z, sekä n kappaleelle muiden luokkien olioita, joilla pitää myös olla pääsy dataan X, Y ja Z.
pistemies kirjoitti:
Noissa isoissa ohjelmissa abstraktia luokkaa on sovellettu siten, että se lataa esim. 20 php-luokkaa itseensä ja jokainen joka abstraktin luokan perii, voi sitten sieltä "periä" haluamansa luokan tai luokat $this->load -metodilla.
Metodien periminen ei ole tässä ongelma. Sen toteutan ihan vain suoraviivaisesti perimällä, se on esteettisempää. Pohdinnan aiheena on se, että kun Luokasta A perityt luokat B, C ja D käyttävät luokalta A perimäänsä metodia x, jolla on luokasta riippumatta sama tehtävä - muodostaa dataa, joka on kaikille sama, eikä riipu siitä kuka sen muodostaa - niin silloin sama data muodostetaan aina uudelleen ja uudelleen aina kun luodaan minkä tahansa A:ta laajentavan luokan olio ja sen haluan välttää.
Triskal kirjoitti:
Tämä on ihan selvä juttu minulle. Yritin kuitenkin kalastella vielä vähän kouriintuntuvampaa perustelua sille miksi luokan pitäisi olla abstrakti.
No jos sulla on esimerkiksi sivumoottori, josta sivut on tarkoitus periä, niin eihän se sivumoottori itsessään tee yhtään mitään. Eli jos loisit olion siitä sivumoottorista, niin sillä oliolla ei olisi mitään virkaa ja näin ollen on järkevää ilmaista se suoraan määrittämällä se abstraktiksi.
Triskal kirjoitti:
kun Luokasta A perityt luokat B, C ja D käyttävät luokalta A perimäänsä metodia x, jolla on luokasta riippumatta sama tehtävä - muodostaa dataa, joka on kaikille sama, eikä riipu siitä kuka sen muodostaa - niin silloin sama data muodostetaan aina uudelleen ja uudelleen aina kun luodaan minkä tahansa A:ta laajentavan luokan olio ja sen haluan välttää.
No eikö silloin olisi järkevämpää tehdä B, C ja D -luokat niin, että ne eivät peri A-luokkaa, vaan A-luokka on yksi staattinen luokka jota muut käyttävät. Siis jos kerran A-luokan datan ei kuulu vaihdella eri olioissa. Tai jos osa A-luokasta on kaikille yhteistä ja osa olioittain vaihtelevaa niin jakaa sen kahdeksi erilliseksi luokaksi, joista toinen peritään ja toinen on staattinen jota kaikki käyttää.
Grez kirjoitti:
No eikö silloin olisi järkevämpää tehdä B, C ja D -luokat niin, että ne eivät peri A-luokkaa, vaan A-luokka on yksi staattinen luokka jota muut käyttävät. Siis jos kerran A-luokan datan ei kuulu vaihdella eri olioissa. Tai jos osa A-luokasta on kaikille yhteistä ja osa olioittain vaihtelevaa niin jakaa sen kahdeksi erilliseksi luokaksi, joista toinen peritään ja toinen on staattinen jota kaikki käyttää.
Eli staattisuus FTW. Taidanpa ottaa mallia tuosta aikaisemmin peistaamastani DB-wrapperista.
Staattisuus ei todellakaan ole "ftw". Sillä saa yksinkertaisissa systeemeissä ihan siistiä jälkeä tietyillä osa-alueilla, mutta kokonaista järjestelmää et voi rakentaa pelkistä singletoneista. (Olen joutunut todistamaan, että kyllä niinkin voi tehdä, mutta älä oikeasti...)
Kannattaisi tosiaan ladata jonkin valmiin - JÄRKEVÄSTI KOODATUN - järjestelmän koodit ja tutkiskella niitä. Tai vaikka unohtaa oman kehysympäristön pykääminen ja ladata suoraan vaikkapa CakePHP tai jokin toinen frameworkki, joka tarjoaa peruspalikat valmiina.
PHP-koodaamisen tila on minusta surullinen siinä mielessä, että hyvin usein koodarit alkavat vääntää kaikkea itse ja unohtavat valmiiden kirjastojen ja kehysympäristöiden olemassaolon. Alkaisiko kukaan vääntää paljasta JavaScriptiä tai C++:aa sen sijaan että käyttäisi jQueryä tai Boostia tai mitä sitten tarvitseekaan?
The Alchemist kirjoitti:
mutta kokonaista järjestelmää et voi rakentaa pelkistä singletoneista.
Kuka sanoo, että aion tehdä niin?
The Alchemist kirjoitti:
Tai vaikka unohtaa oman kehysympäristön pykääminen ja ladata suoraan vaikkapa CakePHP tai jokin toinen frameworkki, joka tarjoaa peruspalikat valmiina.
Vakavasti harkitsenkin sitä aina välillä. Mutta tämä on minulle hupiprojekti, mikä tarkoittaa, että saan tehdä niinkuin tykkään.
Elähän nyt suotta provosoidu, kun en ole yrittänyt ketään ärsyttääkään.
Olen itse nyt siinä kehityksen vaiheessa, että minua on suorastaan alkanut vituttaa aina säätää omiani, koska silloin vain tuhlaa aikaa kaikkeen epäoleelliseen ja itse projekti kuivuu kasaan ennen kuin edistys alkaa näkyä ulospäin.
Ja kun "vankasta kokemuksesta" huolimatta tässä puidaan ihan ohjelmistosuunnittelun alkeista, ei edes perusteista, niin minusta olisi parempi keskittyä oppimiseen tekemisen sijaan näin aluksi. Tässä viestiketjussa ei todellakaan ole vielä perehdytty ollenkaan siihen, miten tietojärjestelmiä kannattaisi alkaa kehittää tai että miten edes joku pieni osanen sellaisesta kannattaisi toteuttaa.
tl;dr:
Triskal kirjoitti:
... olio B tarvitsee kyseisistä asetuksista jotain, mutta koska se on "samalla tasolla" kuin A::asetukset, se ei pääse niihin käsiksi. Mikä on fiksuin tapa ratkaista tämä?
Viemällä A::asetukset b:lle. Dependency Injection on pop.
The Alchemist kirjoitti:
Kannattaisi tosiaan ladata jonkin valmiin - JÄRKEVÄSTI KOODATUN - järjestelmän koodit ja tutkiskella niitä.
Minä toteuttaisin tuon homman juuri noin. Olen parhaillaankin tekemässä yhtä ohjelmaa, joka on lähes valmis. Helpottaa kummasti kun on valmis moottori, jonka ympärille voi rakentaa vaikka minkälaisia luokkia ja sitä kautta kukin alaluokka pysyy järkevästi kasassa ja liittyy tiiviisti kokonaisuuteen.
Luokista ei kannata tehdä kauhean suuria, jotta koodi säilyy selvänä.
The Alchemist kirjoitti:
Olen itse nyt siinä kehityksen vaiheessa, että minua on suorastaan alkanut vituttaa aina säätää omiani, koska silloin vain tuhlaa aikaa kaikkeen epäoleelliseen ja itse projekti kuivuu kasaan ennen kuin edistys alkaa näkyä ulospäin.
Itse puolestani olen siinä kehityksen vaiheessa, että ketuttaa aina puukottaa, purkkapaikata ja kierrellä muiden tekemisiä ja haluaisin kerrankin tehdä jotain omaa. Kuten sanoin, teen tätä koska voin ja koska se on minusta hauskaa, enkä oikein ymmärrä miksi kyseenalaistat koko ajan.
The Alchemist kirjoitti:
Ja kun "vankasta kokemuksesta" huolimatta tässä puidaan ihan ohjelmistosuunnittelun alkeista, ei edes perusteista, niin minusta olisi parempi keskittyä oppimiseen tekemisen sijaan näin aluksi.
Tekemällä oppii, kuuluu vanha viisaus. Tämä on minun oppimisprosessiani - opin käytännön haasteiden ja niiden ratkaisemisen kautta. Onko siinä jotain väärää?
Lisäys:
tsuriga kirjoitti:
Viemällä A::asetukset b:lle. Dependency Injection on pop.
Tästä en olekaan ennen kuullut. EDIT: Hö, nimi sai sen kuulostamaan tietoturvariskiltä, vrt. SQL injection :D Luin pikasilmäilyllä jokusen artikkelin aiheesta singleton vs dependency injection (esim http://goo.gl/jmx5v) ja tajusin, että olen tehnyt dependency injection -tyylillä koko ajan, tiedostamatta että kyseisellä konseptilla on nimi. Omat fiilikset kyseisestä vastakkainasettelusta ovat vähän ristiriitaiset. Toisaalta tykkään kurinalaisesta DI-tavasta, toisaalta singleton tuntuisi äkkiseltään säästävän vaivaa ja vastaavan paremmin omaan konseptuaaliseen näkemykseeni siitä että vain yksi instanssi pitäisi olla alustettuna, jos se on aina sama. Entäs sitten jos dependenssinä käyttää koko container-luokkaa? Olen tehnyt esim näin:
class A { var d; var x; function __construct() { $this->d = new Dependency(); $this->x = new SomethingElse(); $this->x->assignDependency($this); } function aMethodEverybodyNeeds() { } }
Tuo Book-esimerkki näyttää kyllä aika omituiselta ts. eihän domain-oliolla voi olla omaa tietokantayhteyttä (tai tietokantayhteyttä ylipäätänsä), vaan sitten pitäisi olla jokin erillinen BookMapper-luokka. Mutta ideallisesti tuota kannattaa käyttää, jos osaa käyttää sitä oikein.
Triton kirjoitti:
ts. eihän domain-oliolla voi olla omaa tietokantayhteyttä
Hohhoijaa, kun en taaskaan näköjään tiedä mistään mitään. Selittäisitkö mitä tarkoitat domain-oliolla?
Tuo class A esimerkki näyttää hieman oudolta, liekkö Triton tarkoittanut tätä samaa.
DI:ssä viet riippuvuudet suoraan constructorissa tai setterillä tjms.
class A { protected $d = null public function __construct(D $d) { $this->d = $d; } ...
Eli jos A tarvitsee D:tä, viet D:n sinne suoraan constructorissa. Jos D on A:lle valinnainen, voit käyttää constructor-injektion sijaan setter-injektiota (teet A:lle julkisen metodin millä asetat D:n).
Yleensä ei luoda uusia instansseja eri tilanteita varten, eli jos D:tä käytään useammassa paikassa, niin sama kertaalleen luotu D:n instanssi viedään sinne missä sitä tarvitaan. Tässä ei ole sitä pelkoa että samasta luokasta luodaan instansseja hurjaa tahtia kaikkialle missä sitä tarvitaan (jos tätä tuolla aiemmin tarkoitit).
Tuosta pääset sitten DI-containereihin, eli käytännössä automatisoit injektoimisen (ja konfiguroinnin).
Tästä saa samalla iskulla selkeän esimerkin abstraktien luokkien käyttömahdollisuudesta. Jos sinulla on esim. kaksi luokkaa mitkä ottavat riippuvuutena tietyn objektin, sanotaan vaikka A ja B molemmat käyttävät C:tä. Nyt voit näppärästi tehdä abstraktin luokan D mikä sisältää setterin mikä ottaa vastaan C:n:
setC(C $c); $this->c = $c;
Nyt A ja B voivat periä D:n ja molemmat saavat käyttöönsä C:n (ja tämä C konfiguroidaan vain kertaalleen, joten sitä ei tarvitse injektoida erikseen A:lle ja B:lle).
No tuolta viestiltäni tippui pohja aika pitkälti pois sen jälkeen, kun mainitsemani esimerkkikoodi katosi. Sinänsä se ei liittynyt tähän varsinaiseen keskusteluun DI:stä vaan siihen, että minusta on epäloogista, jos Book-oliolla on (ts. oli) minkäänlaista yhteyttä tietokantaan. Näkemykseni mukaan ohjelman domain-oliot (eli oliot, jotka mallintavat sovelluskohtaista käsitteistöä) elävät ikään kuin omassa maailmassaan, joita sitten ns. pragmaattisemmat oliot käyttävät varsinaisen tehtävän suorittamiseen.
Okei, nyt kun tiedän itsekin mitä pitää kysyä, niin selvennän vielä hieman ajattelumalliani. Se on tällainen:
//index.php - mulle on tosi tärkeää, että entry point on selkeä $website = new Website(); $website->run(); class Website { function __construct() { $this->page = new Page(); // Web-sivusto koostuu sivuista } function run() { // Tehdään mitä tehdään. } } class Page() { function __construct() { $this->content[] = new ContentElement('foo'); // Web-sivu koostuu sisältöelementeistä $this->content[] = new ContentElement('bar'); } }
Nyt aloin miettiä, että ajattelenkohan tarpeettomasti sisältä ulospäin tilanteessa, jossa pitäisi ajatella ulkoa sisäänpäin? Mitäs jos rakenne menisikin näin:
//index.php $app = new Application(); $app->run(); class Application { function __construct() { $this->website = new Website(); $this->website->setPage($this->determinePage()); $this->website->page->setContent($this->getContentElementsForPage()); } function determinePage() { return new Page('Title'); } function getContentElementsForPage() { return new Whatever(); } function run() { } }
Toteuttaisi ainakin Tritonin mainitsemaa prinsiippiä paremmin, eivätkä luokat olisi itsessään dependenssejä niille luokille, joista ne itse luovat ilmentymiä.
Miksi tehdä tarpeettomia riippuvuuksia luokkien kesken? (jos ei ole rajapinta kyseessä)
Eikös ole parempi lähteä siitä että luokka tekee asioita mahdollisimman modulaarisesti riippumatta muista luokista.?
Grez kirjoitti:
Luokan abstraktius vain kertoo, että siitä itsestään ei ole tarkoitus tehdä olioita.
Noin käytettynä, luokan abstraktius antaa olettaa että luokka pitää sisällään myös abstrakteja metodeja. Jos ei ole abstrakteja metodeja, niin private constructor on mielestäni parempi tapa estää luokkaa ilmentymastä.
qeijo kirjoitti:
Miksi tehdä tarpeettomia riippuvuuksia luokkien kesken? (jos ei ole rajapinta kyseessä)
Tarpeettomista en tiedä, mutta riippuvuuksia kannattaa tehdä helpottamaan testausta ja selkeyttämään ohjelmalogiikkaa (Google Tech Talk: OO Design for Testability).
Katsoin itse asiassa juuri tuon videon tässä aikaisemmin päivällä. Erittäin valaisevaa kamaa.
Aihe on jo aika vanha, joten et voi enää vastata siihen.