2017-11-17 94 views
6

proszę sprawdzić następujące klasy, że mam stworzyć zbudować kilka klatek XML ..rozszerzyć lub wdrożyć SimpleXML uniknąć DRY

class CommandBuilder 
{ 
    public function __construct() 
    { 
     // 
    } 

    public function login($username, $password) 
    { 
     $frame = $this->frame(); 

     $command = $frame->addChild('command'); 
     $login = $command->addChild('login'); 
     $login->addChild('username', $username); 
     $login->addChild('password', $password); 
     $command->addChild('authKey', 'authkey'); 

     return $frame->asXML(); 
    } 

    public function info($id) 
    { 
     $frame = $this->frame(); 

     $command = $frame->addChild('command'); 
     $login = $command->addChild('product'); 
     $login->addChild('id', $id); 
     $command->addChild('authKey', 'authkey'); 

     return $frame->asXML(); 
    } 

    protected function frame() 
    { 
     return new SimpleXMLElement(
      '<app/>' 
     ); 
    } 
} 

Jaki jest najlepszy sposób, aby uniknąć powielania $frame->addChild('command') i $command->addChild('authKey', 'authkey') bez kolejność elementów zmienia?

Proszę, pomóż ulepszyć kod. Dzięki

Odpowiedz

0

Mimo że kod można uprościć, istnieją komplikacje. Sposób, w jaki to zrobiłem, przenosi konstrukcję ramki do metody frame. Każda z pozostałych procedur buduje podstawę węzła <command> i przekazuje ją dalej, metoda ramki następnie dodaje bit authkey. Ten kod będzie zrobić to samo - ale to musi być zrobione na wszystkich klatek ...

class CommandBuilder 
{ 
    public function __construct() 
    { 
     // 
    } 

    public function login($username, $password) 
    { 
     $command = new SimpleXMLElement('<command />'); 
     $login = $command->addChild('login'); 
     $login->addChild('username', $username); 
     $login->addChild('password', $password); 

     return $this->frame($command); 
    } 

    public function info($id) 
    { 
     $command = new SimpleXMLElement('<command />'); 
     $login = $command->addChild('product'); 
     $login->addChild('id', $id); 

     return $this->frame($command); 
    } 

    protected function frame($node) { 
     $node->addChild('authKey', 'authkey'); 
     $xml = new DOMDocument(); 
     $xml->loadXML('<app/>'); 
     // Convert SimpleXML to DOMDocument 
     $fromDom = dom_import_simplexml($node); 
     // Add in the $node passed in to the frame 
     $xml->documentElement->appendChild($xml->importNode($fromDom, true)); 

     return $xml->saveXML(); 
    } 
} 
0

Wygląda na to twoi zależności nie są zbyt pomieszane więc jest to dość prosty przykład, gdzie można przenieść ten do własnej metody. Pamiętaj, że w PHP obiekty są przekazywane przez odniesienie. Oznacza to, że zmienna obiektowa jest w rzeczywistości tylko wskaźnikiem pamięci do obiektu i kontrastuje z tym, jak domyślnie przekazywane są zmienne skalarne i tablicowe (według wartości ... aka brak wskaźnika pamięci).

Na wynos jest to, że obiekt jest zawsze taki sam, bez względu na to, gdzie jest używany, o ile nie jest to clone.

<?php 
class CommandBuilder 
{ 
    public function preBuild(\SimpleXMLElement $node) 
    { 
     $command = $node->addChild('command'); 
     $command->addChild('authKey', 'authkey'); 
    } 
} 

Teraz, zamiast wywoływania tych dwóch metod, można po prostu zadzwonić $this->preBuild($frame).

2

Jak o czymś takim, gdzie można utworzyć osobną klasę producentów:

class CommandBuilder 
{ 
    private $commandName; 

    private $params = []; 

    public function __construct($commandName) { 
    $this->commandName = $commandName; 
    } 

    // convenience method, to allow for cleaner fluent interface usage 
    public static function create($commandName) { 
    return new self($commandName); 
    } 

    public function addParam($paramName, $paramValue) { 
    $this->params[] = [ 'name' => $paramName, 'value' => $paramValue ]; 

    return $this; 
    } 

    public function build() { 
    $app = new SimpleXMLElement('<app/>'); 
    $commandContainer = $app->addChild('command'); 
    $command = $commandContainer->addChild($this->commandName); 
    foreach($this->params as $param) { 
     $command->addChild($param[ 'name' ], $param[ 'value' ]); 
    } 
    $commandContainer->addChild('authKey', 'authKey'); 

    return $app->asXML(); 
    } 
} 

a potem mieć osobną klasę, która buduje konkretne polecenia aplikacji:

class AppCommands 
{ 
    public function login($username, $password) { 
    return CommandBuilder::create('login')->addParam('username', $username) 
              ->addParam('password', $password) 
              ->build(); 
    } 

    public function info($id) { 
    return CommandBuilder::create('product')->addParam('id', $id) 
               ->build(); 
    } 
} 

Wykorzystanie pozostaje taka sama, z wyjątkiem tworzenia instancji AppCommands, zamiast CommandBuilder:

$ac = new AppCommands; 

echo $ac->login('MyUsername', 'MyPassword'); 
echo PHP_EOL; 
echo $ac->info(5); 

View this example on eval.in

Jeśli chcesz, możesz oczywiście również dynamicznie przekazać authKey do CommandBuilder zamiast twarde kodowania go w środku, coś jak:

class CommandBuilder 
{ 
    private $commandName; 

    private $authKey; 

    private $params = []; 

    public function __construct($commandName, $authKey) { 
    $this->commandName = $commandName; 
    $this->authKey = $authKey; 
    } 

    public static function create($commandName, $authKey) { 
    return new self($commandName, $authKey); 
    } 

    /* ... */ 

    public function build() { 

    /* ... */ 

    $commandContainer->addChild('authKey', $this->authKey); 

    return $app->asXML(); 
    } 
0

Jak o napisanie „szkielet "metoda, która akceptuje parametr jako wywoływalny i wykonuje go w środku, tak jak to. (Wadą jest to, że teraz parametry, takie jak $username i $password, są powtarzane.)

class CommandBuilder 
{ 
    // ... 

    private function createCommand(callable $cmdEnricher) 
    { 
     $frame = $this->frame(); 

     $command = $frame->addChild('command'); 
     $cmdEnricher($command); 
     $command->addChild('authKey', 'authkey'); 

     return $frame->asXML(); 
    } 

    public function login($username, $password) 
    { 
     return $this->createCommand(function ($command) use ($username, $password) { 
      $login = $command->addChild('login'); 
      $login->addChild('username', $username); 
      $login->addChild('password', $password); 
     }); 
    } 

    public function info($id) 
    { 
     return $this->createCommand(function ($command) use ($id) { 
      $login = $command->addChild('product'); 
      $login->addChild('id', $id); 
     }); 
    } 

    // ... 
}