Moose: Porovnání verzí

Z Wikiverzity
Smazaný obsah Přidaný obsah
Kychot (diskuse | příspěvky)
Vrácení omylem vymazaného textu: http://cs.wikiversity.org/w/index.php?title=Moose&curid=7767&diff=30095&oldid=30094
m Robot: Náhrada zastaralého tagu
 
Řádek 46: Řádek 46:


Sytému Moose je věnována kapitola ''Objects'' v knize [[Modern Perl#07. Objekty]]. Touto kapitolou je inspirován tento tutoriál. Poznamenejme, že direktivami
Sytému Moose je věnována kapitola ''Objects'' v knize [[Modern Perl#07. Objekty]]. Touto kapitolou je inspirován tento tutoriál. Poznamenejme, že direktivami
<source lang='perl'>
<syntaxhighlight lang='perl'>
use utf8;
use utf8;
binmode STDOUT, ':utf8';
binmode STDOUT, ':utf8';
</syntaxhighlight>
</source>
můžeme (pro legraci) používat i české názvy proměnných i s diakritikou (pro vážné programování bych to ale asi nedoporučoval).
můžeme (pro legraci) používat i české názvy proměnných i s diakritikou (pro vážné programování bych to ale asi nedoporučoval).


Z nějakých důvodů to ale s diakritikou nefunguje v případě, že tak nazveme třídu; příkaz
Z nějakých důvodů to ale s diakritikou nefunguje v případě, že tak nazveme třídu; příkaz


<source lang='perl'>
<syntaxhighlight lang='perl'>
package Kočka
package Kočka
</syntaxhighlight>
</source>
vyvolá chybu:
vyvolá chybu:
Kočka is not a module name at /usr/lib/perl5/Class/MOP/Package.pm line 207.
Kočka is not a module name at /usr/lib/perl5/Class/MOP/Package.pm line 207.
Řádek 75: Řádek 75:
I když se nejedná o nějaký předpis jazyka Perl, jde o určitou konvenci, že jména tříd začínají velkými písmeny (třída je jakoby větší, protože může mít mnoho instatncí), zatímco jména instancí začínají malými písmeny, tedy je: ''Kocka líza''. (Je to přesně naopak než v češtině, angličtině i jiných jazycích, kde píšeme ''kočka Líza.) Pokud se nám to nelíbí, nemusíme samozřejmě tuto konvenci dodržovat.
I když se nejedná o nějaký předpis jazyka Perl, jde o určitou konvenci, že jména tříd začínají velkými písmeny (třída je jakoby větší, protože může mít mnoho instatncí), zatímco jména instancí začínají malými písmeny, tedy je: ''Kocka líza''. (Je to přesně naopak než v češtině, angličtině i jiných jazycích, kde píšeme ''kočka Líza.) Pokud se nám to nelíbí, nemusíme samozřejmě tuto konvenci dodržovat.


<source lang='perl' line>
<syntaxhighlight lang='perl' line>
#! /usr/bin/perl
#! /usr/bin/perl


Řádek 98: Řádek 98:
$líza->mňau();
$líza->mňau();
$mňouk->mňau();
$mňouk->mňau();
</syntaxhighlight>
</source>


*1. Říkáme shellu, který program má interpretovat tento skript.
*1. Říkáme shellu, který program má interpretovat tento skript.
Řádek 127: Řádek 127:
Tyto tzv. ''atributy'' (někdy se jim říká ''stav'' anebo prostě data dané instance) se v ''Mose'' přikládají pomocí funkce <tt>has()</tt>; zde je možné rovněž určit, jestli je možné tento atribut dále měnit a jaký typ proměnné bude použit, což představuje určitý typ kontroly nad tím, jak budeme moci s objektem nakládat.
Tyto tzv. ''atributy'' (někdy se jim říká ''stav'' anebo prostě data dané instance) se v ''Mose'' přikládají pomocí funkce <tt>has()</tt>; zde je možné rovněž určit, jestli je možné tento atribut dále měnit a jaký typ proměnné bude použit, což představuje určitý typ kontroly nad tím, jak budeme moci s objektem nakládat.


<source lang='perl' line>
<syntaxhighlight lang='perl' line>
#! /usr/bin/perl
#! /usr/bin/perl


Řádek 149: Řádek 149:
$tlusťoch->dieta( 'neslaná');
$tlusťoch->dieta( 'neslaná');
say $tlusťoch->jméno(), ', ', $tlusťoch->věk(), ' let, nová dieta: ', $tlusťoch->dieta();
say $tlusťoch->jméno(), ', ', $tlusťoch->věk(), ' let, nová dieta: ', $tlusťoch->dieta();
</syntaxhighlight>
</source>


* 11. atribut ''jméno'' je read-only a je to string
* 11. atribut ''jméno'' je read-only a je to string
Řádek 171: Řádek 171:
Příklad: Namísto toho, abychom zadávali věk kočky, zadáme datum jejího narození a její věk se pak automaticky spočítá. Pozměníme třídu Kocka:
Příklad: Namísto toho, abychom zadávali věk kočky, zadáme datum jejího narození a její věk se pak automaticky spočítá. Pozměníme třídu Kocka:


<source lang='perl'>
<syntaxhighlight lang='perl'>
{
{
package Kocka;
package Kocka;
Řádek 186: Řádek 186:
}
}
}
}
</syntaxhighlight>
</source>


Pomineme, že věk zde počítáme jen přibližně (zanedbáváme datum narození), ale co je důležité:
Pomineme, že věk zde počítáme jen přibližně (zanedbáváme datum narození), ale co je důležité:
# změnil se popis třídy: namísto atributu ''věk'' je zde uložen atribut ''rok narození'' a je definována metoda na výpočet věku
# změnil se popis třídy: namísto atributu ''věk'' je zde uložen atribut ''rok narození'' a je definována metoda na výpočet věku
# způsob volání se přitom ale nezměnil – věk dané kočky zjistíme stejně jako předtím:
# způsob volání se přitom ale nezměnil – věk dané kočky zjistíme stejně jako předtím:
<source lang='perl'>
<syntaxhighlight lang='perl'>
say $tlusťoch->věk();
say $tlusťoch->věk();
</syntaxhighlight>
</source>
Tak to je ta enkapsulace: Zatímco se nějaký vnitřní mechanismus třídy změnil, zvenku se to chová stále stejně; při volání metody nás jakoby nemusí zajímat, jak je to uvnitř té třídy zařízeno, že to tak funguje, hlavně, že to funguje.
Tak to je ta enkapsulace: Zatímco se nějaký vnitřní mechanismus třídy změnil, zvenku se to chová stále stejně; při volání metody nás jakoby nemusí zajímat, jak je to uvnitř té třídy zařízeno, že to tak funguje, hlavně, že to funguje.


Další výhoda takového přístupu je, že si můžeme definovat nějaké defaultní hodnoty, které konstruktor objektu použije v případě, že nějaké atributy nejsou zadány explicitně; například předpokládáme, že zakládáme rodný list kočky a tím pádem můžeme předpokládat, že pokud nezadáme její rok narození, bude se jednat o nynější rok; pak defaultní hodnotu tohoto atributu definujeme např. následovně:
Další výhoda takového přístupu je, že si můžeme definovat nějaké defaultní hodnoty, které konstruktor objektu použije v případě, že nějaké atributy nejsou zadány explicitně; například předpokládáme, že zakládáme rodný list kočky a tím pádem můžeme předpokládat, že pokud nezadáme její rok narození, bude se jednat o nynější rok; pak defaultní hodnotu tohoto atributu definujeme např. následovně:
<source lang='perl'>
<syntaxhighlight lang='perl'>
has 'rok_narození', is=>'ro', isa=>'Int',
has 'rok_narození', is=>'ro', isa=>'Int',
default => sub{(localtime)[5] + 1900};
default => sub{(localtime)[5] + 1900};
</syntaxhighlight>
</source>


<source lang='perl'>
<syntaxhighlight lang='perl'>
</syntaxhighlight>
</source>


[[Kategorie:Perl]]
[[Kategorie:Perl]]

Aktuální verze z 21. 4. 2020, 09:46

Moose je objektový systém, vytvořený jako knihovna v Perlu na knihovnou Class::MOP. V podstatě vytváří určitý způsob myšlení (školu, anticipující Perl6), jak zacházet s objekty v Perlu (s objekty se zde jinak dá zacházet celkem volně).

Info[editovat]

Související články[editovat]

  • KiokuDB – persistentní objekty založené na Moose jsou transparentně ukládány pomocí různých databázových backendů

Externí odkazy[editovat]

Instalace[editovat]

Ubuntu[editovat]

Při výběru balíku libmoose-perl budou nainstalovány i následující balíky:

  • libb-hooks-endofscope-perl (verze 0.08-1) bude nainstalován
  • libclass-load-xs-perl (verze 0.03-1) bude nainstalován
  • libdevel-globaldestruction-perl (verze 0.04-2) bude nainstalován
  • libdevel-partialdump-perl (verze 0.15-1) bude nainstalován
  • libeval-closure-perl (verze 0.06-1) bude nainstalován
  • libmoose-perl (verze 2.0401-1) bude nainstalován
  • libnamespace-clean-perl (verze 0.22-1) bude nainstalován
  • libsub-exporter-perl (verze 0.982-1) bude nainstalován
  • libsub-identify-perl (verze 0.04-1build2) bude nainstalován
  • libtask-weaken-perl (verze 1.03-1) bude nainstalován
  • libvariable-magic-perl (verze 0.47-1build1) bude nainstalován


Tutoriál[editovat]

Sytému Moose je věnována kapitola Objects v knize Modern Perl#07. Objekty. Touto kapitolou je inspirován tento tutoriál. Poznamenejme, že direktivami

use utf8;
binmode STDOUT, ':utf8';

můžeme (pro legraci) používat i české názvy proměnných i s diakritikou (pro vážné programování bych to ale asi nedoporučoval).

Z nějakých důvodů to ale s diakritikou nefunguje v případě, že tak nazveme třídu; příkaz

package Kočka

vyvolá chybu:

Kočka is not a module name at /usr/lib/perl5/Class/MOP/Package.pm line 207.
BEGIN failed--compilation aborted at ./07.01Kocka.pl line 9.

Nejspíš se jedná o bug – viz Pragmas > utf8 až na konci: Bugs.

01. Konstruktory – stvoření koček[editovat]

Tak tedy, vytvoříme si třídu koček, které umí mňoukat, a dvě její instance, Lízu a Mňauka, které pak z mňoukání vyzkoušíme. Ale ještě k terminologii, která je v objektovém programování celkem obvyklá:

  • Třída je v Perlu v podstatě to samé, co balík. Má to logiku, zpravidla definice tříd jsou i v jiném (např. knihovním) souboru *.pm, ale klidně mohou být i v tom samém souboru. Hlavně to ale znamená, že všechny proměnné tam jsou plně kvalifikované, tj. mají vlastní jmenný prostor, daný názvem třídy.
  • Konkrétní instance se vytvářejí pomocí konstruktoru new
  • metoda je funkce, asociovaná s třídou. Když se volá, má vždy nějaký invokant, se kterým metoda pracuje:
    • když vytváříme nový objekt, je tím invokantem jméno třídy
    • když voláme metodu u konkrétní instance, je tím invokantem tato instance.
  • Invokant se v Perlu předává funkci jakožto první argument.

I když se nejedná o nějaký předpis jazyka Perl, jde o určitou konvenci, že jména tříd začínají velkými písmeny (třída je jakoby větší, protože může mít mnoho instatncí), zatímco jména instancí začínají malými písmeny, tedy je: Kocka líza. (Je to přesně naopak než v češtině, angličtině i jiných jazycích, kde píšeme kočka Líza.) Pokud se nám to nelíbí, nemusíme samozřejmě tuto konvenci dodržovat.

#! /usr/bin/perl

use Modern::Perl;
use utf8;
binmode STDOUT, ':utf8';

{
    package Kocka;
    use Moose;

    sub mňau
    {
        my $self = shift;
        say 'Mňau!';
    }
}

my $líza = Kocka->new();
my $mňouk = Kocka->new();

$líza->mňau();
$mňouk->mňau();
  • 1. Říkáme shellu, který program má interpretovat tento skript.
  • 2. Programujeme ve stylu Modern::Perl.
  • 4. Co je ve skriptu, bude bráno jako znaky UTF-8
  • 5. Co půjde na standardní výstup, bude chápáno také jako text v UTF-8
  • 7. Začneme nový blok (složená závorka), protože to bude nový balík s vlastním jmenným prostorem.
  • 8. Vytvářím třídu Kocka.
  • 9. Používám k tomu knihovnu Moose. (Tím je dané např. i implicitní vytvoření konstruktoru new.)
  • 11. Podprogram, přesněji řečeno metoda mňau, kde říkám, jak má libovolná kočka mňoukat.
  • 13. Uložíme invokant (tedy první parametr funkce) do proměnné $self.
  • 14. Tady konečně říkám, jak má kočka mňoukat.
  • 18.–19. Postupně vytvářím dvě instance koček, tedy dva konkrétní případy, kočky Lízu a Mňouka.
  • 21.-22. Vyzkouším si, jak mňoukají.

Výstup skriptu bude jednoduchý:

Mňau!
Mňau!

Někdo namítne, že je to nedokonalé, že Mňouk je kocour a ten že mňouká jinak. Námitka je na místě, časem zjistíme, že je možno vytvářet i různá pohlaví, která budou mňoukat jinak.

02. Atributy[editovat]

Když už vytváříme takovéto kočky, bylo by dobré, aby samy kočky věděly, jak se jmenují a mohly se nám na požádání třeba představit. Tedy všechny kočky (tj. třída koček) by měla mít nějaký šuplíček, kam by se ukládalo jméno té které kočky. A pak třeba její věk (kolik je jí let) a jestli třeba nemá nějakou dietu (třeba vytváříme databázi pacientů v kočičí nemocnici).

Tyto tzv. atributy (někdy se jim říká stav anebo prostě data dané instance) se v Mose přikládají pomocí funkce has(); zde je možné rovněž určit, jestli je možné tento atribut dále měnit a jaký typ proměnné bude použit, což představuje určitý typ kontroly nad tím, jak budeme moci s objektem nakládat.

#! /usr/bin/perl

use Modern::Perl;
use utf8;
binmode STDOUT, ':utf8';

{
    package Kocka;
    use Moose;

    has 'jméno', is=>'ro', isa=>'Str';
    has 'věk',   is=>'rw', isa=>'Int';
    has 'dieta', is=>'rw';
}

my $tlusťoch = Kocka->new(jméno=>'Tlusťoch', věk=8, dieta=>'produkty moře');

say $tlusťoch->jméno(), ', ', $tlusťoch->věk(), ' let, dieta: ', $tlusťoch->dieta();

$tlusťoch->dieta( 'neslaná');
say $tlusťoch->jméno(), ', ', $tlusťoch->věk(), ' let, nová dieta: ', $tlusťoch->dieta();
  • 11. atribut jméno je read-only a je to string
  • 12. atribut věk je read/write a je to číslo
  • 13. atribut dieta je read/write a není řečeno, jaký typ proměnné to bude
  • 16. vytvoříme tlusťocha, který se jmenuje Tlusťoch, je mu 8 let a má speciální dietu
  • 18. vypíšeme výše zadané informace o tlusťochovi a to pomocí samočinně vytvořených metod (kterým se říká akcesory), které se volají bez argumentů (prázdné závorky)
  • 20. změníme mu dietu pomocí metody dieta, která se automaticky vytvořila právě z účelem změny atributu; formálně je shodná s akcesorem, akorát že se volá s příslušným argumentem; takové metodě se pak říká mutátor
  • 21. znovu vypíšeme status

Výstup programu:

Tlusťoch, 8 let, dieta: produkty moře
Tlusťoch, 8 let, nová dieta: neslaná

03. Enkapsulace[editovat]

Enkapsulace neboli zapouzdření znamená, že se skryjí vnitřní detaily objektu.

Příklad: Namísto toho, abychom zadávali věk kočky, zadáme datum jejího narození a její věk se pak automaticky spočítá. Pozměníme třídu Kocka:

{
    package Kocka;
    use Moose;

    has 'jméno', is=>'ro', isa=>'Str';
    has 'dieta', is=>'rw';
    has 'rok_narození',  is=>'ro', isa=>'Int';

    sub věk {
        my $self = shift;
        my $rok = (localtime)[5] + 1900;
        return $rok - $self->rok_narození;
    }
}

Pomineme, že věk zde počítáme jen přibližně (zanedbáváme datum narození), ale co je důležité:

  1. změnil se popis třídy: namísto atributu věk je zde uložen atribut rok narození a je definována metoda na výpočet věku
  2. způsob volání se přitom ale nezměnil – věk dané kočky zjistíme stejně jako předtím:
say $tlusťoch->věk();

Tak to je ta enkapsulace: Zatímco se nějaký vnitřní mechanismus třídy změnil, zvenku se to chová stále stejně; při volání metody nás jakoby nemusí zajímat, jak je to uvnitř té třídy zařízeno, že to tak funguje, hlavně, že to funguje.

Další výhoda takového přístupu je, že si můžeme definovat nějaké defaultní hodnoty, které konstruktor objektu použije v případě, že nějaké atributy nejsou zadány explicitně; například předpokládáme, že zakládáme rodný list kočky a tím pádem můžeme předpokládat, že pokud nezadáme její rok narození, bude se jednat o nynější rok; pak defaultní hodnotu tohoto atributu definujeme např. následovně:

    has 'rok_narození',  is=>'ro', isa=>'Int',
                         default => sub{(localtime)[5] + 1900};