Moose: Porovnání verzí

Z Wikiverzity
Smazaný obsah Přidaný obsah
Kychot (diskuse | příspěvky)
Info
Kychot (diskuse | příspěvky)
Externí odkazy
Řádek 6: Řádek 6:
* [http://moose.perl.org/ moose.perl.org]
* [http://moose.perl.org/ moose.perl.org]
* [http://search.cpan.org/perldoc?Moose search.cpan.org/perldoc?Moose]
* [http://search.cpan.org/perldoc?Moose search.cpan.org/perldoc?Moose]

== Externí odkazy ==
* Linuxsoft.cz – Jiří Václavík (2005–2011): 149dílný seriál [http://www.linuxsoft.cz/article_list.php?id_kategory=210 Perl]:
** [http://www.linuxsoft.cz/article.php?id_article=1683 Perl (107) - Moose - moderní objektový systém]
** [http://www.linuxsoft.cz/article.php?id_article=1687 Perl (108) - Moose - základní vlastnosti]
** [http://www.linuxsoft.cz/article.php?id_article=1688 Perl (109) - Moose - role]
** [http://www.linuxsoft.cz/article.php?id_article=1701 Perl (110) - Moose - meta API]


== Instalace ==
== Instalace ==

Verze z 28. 10. 2012, 18:18

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), jak zacházet s objekty v Perlu (s objekty se zde jinak dá zacházet celkem volně).

Info

Externí odkazy

Instalace

Ubuntu

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

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

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

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

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};