Autor Zpráva
BuGeR
Profil
Ahoj.
Snažím se pochopit řetězení metod v objektech.
Vytvořil jsem si tuto primitivní třídu:
class test {
    var $texty;
    public function text($text) {
        $this->texty[] = $text;
        return $this;
    }
    public function zobraz() {
        foreach($this->texty as $text)
            echo $text."<br />";
    }
}
A vypisují ji takto:
$test = new test;
$test->text("neco1");
$test->text("neco2");
$test->zobraz();

Chci ale docílit toho, že text "neco1", bude tučným písmem.
Nějak takto:
$test->text("neco1")->tucne();
Jak toho mám dosáhnout? Vím, že se nějak musí předat objekt k metodě tucne(), ale vůbec nevím, jak s ním potom pracovat.
Děkuji za případnou pomoc.
Kry5
Profil
Tomu co hledáš se říká float interface a spočívá v tom, že metody vrací "samy sebe" - svůj vlastní objekt tj. this.

Připomínky další:
1) var nepoužívej - použij místo toho něco z public, private, protected - v tvém případě asi bude nejlepší private
2) Bylo by dobré první říct, že $texty je pole (array) než ji přidáš další prvky

Udělal bych to nějak takhle: (jeden objekt drží jeden text, ať je to jednodušší)
<?php
class test { 
    private $text;
    private $bold = false;

      public function addText($text) {
        $this->text = $text; 
        return $this; 
    } 
    public function bold() { 
           $this->bold = true;
           return $this;
     }

       public function zobraz() {
          if($this->bold == true)
                return "<b>".$this->text."</b>";
          else
                return $this->text;
     }
}
Pokud tam nemám chybu tak by použití mohlo vypadat takto:

$t = new test();
echo $t->addText("neco1")->bold()->zobraz();

Nezkoušel jsem to tak nevím jestli to bude fungovat :-)
Mike8748
Profil
Kry5:
Tomu co hledáš se říká float syntaxe
nemyslíš spíš fluent interface ?
BuGeR
Profil
Kry5:
Dobře, děkuji, funguje to perfektně :-)
Ještě tedy dotaz, jak docílít toho, abych těch textu mohl mít v jedné třídě více, a jak můžu s tím textem v metodě bold() pracovat? (např. v budoucnu nepůjde jen o text, ale nějakou složitější metodu).
Díky :-)
Kry5
Profil
Mike: díky, překlep :-)

BuGeR:
Teď úplně nerozumím, jak to myslíš. Pokud chceš mít textů více tak jednoduše upravíš začátek na:
private $text = array();

AddText na:
public function addText($text) { 
        $this->text[] = $text;  
        return $this; 
    }  

Ale teď už je složitější jak s tou metodou bold(). Buďto chceš nastavit všem to bold() a nebo jen jednomu a to už pak nezkombinuješ s float syntaxí. Resp. zkombinuješ, ale musíš předat metodě bold parametr, který text má být tučně. Šlo by udělat i ve fluent interface např. že poslední přidaný dostane tu bold (poslední prvek v array), ale jeden konkrétní text to bys potřeboval znát jeho index v poli. Pak by to šlo udělat tak, že metoda addText bude vracet právě ten index přidaného textu a ten předáš jako parametr funkci bold. Ale to ti jak jsem řekl kazí to fluent interface takže na addText už nebudeš moct navázat další metody.

Samozřejmě by šlo předat metodě bold jako parametr i text toho textu a podle toho by si to našla v poli, ale to je jednak prasácké a jednak to přestává fungovat, když mají dva texty stejný text.
BuGeR
Profil
Aha, dobře. I tak děkuji :-)
Chtěl bych právě funkci bold() nastavit jen některým textům, abych to mohl vypisovat takto:

echo $t->addText("neco1")->bold();
echo $t->addText("neco2");
echo $t->addText("neco3");
echo $t->addText("neco4")->bold();
atd.
Ale i přesto díky :-) zkusím to nějak nastudovat.
Kry5
Profil
A ještě jsem zapomněl v případě toho parametru by ta metoda bold musela taky projít úpravou, protože bold = true/false by přestávalo stačit.

Ještě mě napadlo, že bys přímo v tom addText udával jestli to bude tučně nebo ne např:
<?php
class text() {
  private $texts = array();

   public function addText($text, $bold) {
    $this->texts[] = array("text" => $text, "bold" => $bold);
    return $this;
  }

   public function zobrazVse() { 
    $return = "";
    foreach($this->texts as $text) {
      if($text["bold"] == true)
        $return .= "<b>".$this->text."</b><br>"; 
      else 
         $return .= $this->text."<br>"; 
     }
  }
}

Opět si nejsem jistej jestli je to funkční :-D
BuGeR
Profil
Kry5:
Díky :-) tak to právě ale nechci, nechci to právě všechno nacpat do parametrů jedné metody, ale chtěl bych to právě přehledněji přes jednotlivé metody.
Určitě to nějak musí jít, např. framework Nette to tak tuším má.
Nox
Profil
Kry5:
sjednotil bych jazyk... addText X zobrazVse

BuGeR, Kry5:
Tenhle konkrétní snippet [#6] by musel pracovat tak, že ::addText nevrátí $this, ale nějaký objekt třídy Text, který by měl metodu ::bold
Jinak by se nastavoval bold na celém objektu Test (který by to pak asi nastavil všude ANEBO globálně (není to samé, rozdíl je v tom co s těmi co se přidají pak))
YoSarin
Profil
BuGeR:
šlo by to třeba tak, že při zavolání bold se označí jako tučný poslední - ve třídě pak budeš potřebovat ještě jednu vnitřní promměnou která bude držet informaci o tom které texty jsou bold:
private $bold = array();
public function bold()
{
    end($this->texts);
    $this->bold[key($this->texts)] = true;
    return $this;
}

   public function zobraz()
{
    $out = '';
    foreach ($this->texts as $key => $text) {
        if (!empty($this->bold[$key])) {
            $out .=  '<b>' . $text . '</b><br />';
        } else {
            $out .= $text . '<br />';
        }
    }
    return $out;
}

Ale je otázka jestli pak nezavést nadřazený objekt String (třeba), který bude držet pole textů s jejich příznaky (bold, italic, ...) - v podstatě tak jak píše Nox.
BuGeR
Profil
Děkuji všem, určitě by to nešlo ale nějak jednodušeji? Ve výsledku nebudu mit třeba jen jednu metodu ale třeba 10, toto mi přijde strašně moc komplikované.
Nox
Profil
YoSarin: to bude mít podobná rizika jako tebou neoblíbená reference ;) když ti nějaká funkce mezi addText a bold přidá jiný, nebo prostě se nějakým způsobem změní ukazatel na to poslední, rázem se tam bude dít něco jiného ... ta akce ::bold bude prostě taková ošemetná, bude mít nevynucenou konvenci ji volat hned po ::addText atd. - moc se mi to nelíbí
BuGeR
Profil
Nox:
Plně souhlasím.
Nox
Profil
BuGeR:
Proto právě bude lepší si udělat ještě jeden objekt, kde pak hezky zapouzdříš ty zobrazovací vlastnosti a ten tvůj Test ať je jen kontejner co se stará nějak globálně o skupinu Textů
YoSarin
Profil
Nox:
ta akce ::bold bude prostě taková ošemetná, bude mít nevynucenou konvenci ji volat hned po ::addText
Jo, přesně tak to bude... Lepší varianta by byla mít tu nadřazenou třídu...
BuGeR
Profil
Nox, YoSarin:
Dobře, díky. Chápu tak trochu toerii, ale praxe zůstava zamnou, mohli by jste mě nakopnout nějakým primitivním příkladem?
Kry5
Profil
BuGeR:
To řešení od Noxe je asi nejideálnější.

Když budeš držet místo těch textů přímo objekty můžeš s tím daleko lépe pracovat. Pak addText vrátí ten konkrétní objekt a uloží ho do pole v tom nadřazeném objektu. Ale to už je složitější :-)


Vlastně můžeš využít tu třídu, kterou jsem psal jako první jako ten podřazený objekt a pak uděláš ten nadřazený, který může mít metodu (hrozně jednoduše):
public fuction addText() {
  $o = new text(); //přidání textu dej z metody addText do __construct()
  $this->texts[] = $o;
  return end($this->texts)
}

Opět jsem něco zplácal bude to chtít upravit :-). Takovýhle věci moc často neřešim.
Nox
Profil
Nevim co přesně chceš, ale třeba (možná je to trochu overengineered, nevim jestli je to u tebe důležitý prvek nebo okrajový, to už si pořeš)
interface Container /* něco tam */ {
    public function addElement(Element $element);
    public function removeElement(Element $element);
}

 interface Visualizable {
   public function __toString();
   public function display();
}

 class MyFancyContainer extends Container, Visualizable {
   // ...
}

 interface Element extends Visualizable {}

 interface TextElement extends Element { 
    public function bold();
    public function italic();
    public function normal();
    public function setText($text);
}

 class TextElementImpl extends AbstractElement implements TextElement {
    // ...
}

 $myText = $factory->createTextElement("trust noone")->bold();
$myContainer->addText($myText);

 echo $myContainer;
(nějak tu blbnou mezery...)

Kry5:
new přímo v metodě nebývá ideální; místo end() můžeš vracet přímo $o
BuGeR
Profil
Dobře. Absolutně vůbec to nechápu (:-D) zůstanu asi u klasických funkcí. Přesto díky za Váš čas :-)
Nox
Profil
BuGeR:
4 minuty pokud jsi hned refreshnul a psaní příspěvku v tom a hned to vzdáváš ;)

Pokud nejsi v časovém presu, nechceš to zkusit? Možná to je složitější ... možná je to složitější, než by být muselo, ale - jenom tím že jde člověk za své útulné hranice se něco nového naučí
BuGeR
Profil
Nox:
Chtěl jsem jen způsob, jak pohodlně vytvořit takový zápis: (příklad)
$html->pridejText("BlaBla")->Tucne()->Barva("cervena")->Velikost("16");
Ale nevěděl jsem, že to bude takto složité. Přesto děkuji za váš čas :-)
Nox
Profil
Vždyť jsem ti toho půlku napsal :P

Tak schválně jak dlouho mi to trvalo... (není to samozřejmě testované)
class Container {
   private $texts = array();
   public function addText(Text $text) { $texts[] = $text; return $text; }
   public function __toString() {
       $result = "";
       foreach($this->texts as $text) $result .= $text->render();
       return $result;
   }
}

  class Text {
   const DEFAULT_COLOR = "black";
   const ESCAPE_CSS = "\x00..\x2C./:;<=>?@[\\]^`{|}~";

     private $text = "";
   private $isBold = false;
   private $isItalic = false;
   private $color = Text::DEFAULT_COLOR; // jestli nepojede tak prostě "black", nebo radši null
   
     public function __construct($text) { $this->text = $text; }
   public function bold() { $this->isBold = true; return $this; }
   public function italic() { $this->isItalic = true; return $this; }
   public function color($color) { $this->color = color; return $this; }
   public function normal() { $this->isBold = false; $this->isItalic = false; $this->color = self::DEFAULT_COLOR; return $this; }
  
     public function __toString() { return $this->render(); }
   public function render() {
       $result = "<span style='color: ".addcslashes(htmlspecialchars($this->color), self::ESCAPE_CSS)."'>".$this->text;
       if( $this->isBold ) $result = '<b>'.$result.'</b>';
       if( $this->isItalic ) $result = '<i>'.$result.'</i>';

         return $result;
   }
}

  class Factory { public function createText($text) { return new Text($text); } }

  $html->addText($factory->createText($text))->bold()->italic();
// anebo
$text = $factory->createText($text)->bold()->italic();
$html->addText($text);

  echo $html;
BuGeR
Profil
Nox:
No právě, a to mi přijde strašně komplikované, vytvářet kvůli tomu další třidy.
Ugo
Profil
dá se říct že to je komplikované (mimochodem zmiňoval jsi Nette, tam nic lehčího nenajdeš), ale jen do doby než budeš dělat první úpravu nebo přidání funkčnosti, pak budeš mít úsměv od ucha k uchu, žes to neudělal jinak :)

Když máš všechno jako objekt, tak taky můžeš všechno lehko upravit, to je ta krása objektovýho zapozdření. A taky znovupoužitelnost jinde a po nějaké době je taky úplně jinadší.
Nox
Profil
BuGeR
Tenhle pocit znám, ale myslim že je to fakt iluze.... to založení souboru se třídou je otázka vteřiny, je to hezky rozšiřitelné, přehledné
A bylo to za 10 minut "hotovo"

Samozřejmě každý problém jde řešit mnoha způsoby a taky záleží jak konkrétně TY potřebuješ to řešení komplexní, jestli je to jádro tvého projektu,
anebo věc co se využije jednou za rok atd.

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm:

0