JustPaste.it

Architektura Aplikacji

Wstęp.


Artykuł ten skierowany jest do osób które zaczynają swoją zabawę z programowaniem. Aby zrozumieć w pełni jego treść potrzebna jest znajomość podstawowych wzorców projektowych, oraz podstawowa znajomość php5. Użyty został język php, gdyż jest on bardzo elastyczny i dla przykładów zajmie on dużo mniej miejsca niż inne języki. Artykuł zostanie podzielony na kilka małych części które będą podzielone tematycznie. Nie będę przedstawiał użytych tu narzędzi gdyż dokumentację i tutoriale można znaleźć na oficjalnych stronach , które podaję. Jeśli uważasz , że nie rozumiesz którejś części kodu , nie zrażaj się. Niektóre klasy są po prostu zmyślone , tak aby pokazać sposób rozwiązania problemu a nie całą implementacje kodu. Nie zajmuje się opisaniem co jest wydajniejsze lepsze etc, tylko tym co jest dla ciebie WYGODNIEJSZE. Biorąc pod uwagę nowy sposób programowania - 2 programistów na 1 maszynę , ten artykuł przedstawi ci problematykę pisania naprawdę skomplikowanych aplikacji. W tej części zajmiemy się prostymi aplikacjami. Będzie to kawałek systemu CMS, oraz programu do organizacji faktur. Pamiętaj , nie interesuj się metodą , klasą , tylko rozwiązaniem!

Co to jest projektowanie?


Zaczynając budowę każdej aplikacji najpierw powinniśmy zastanowić się nad jej konstrukcją i sposobem działania. To inaczej mówiąc jest właśnie projektowanie. Nie ogranicza się ono jedynie do zakreślenia, jakiej bazy danych będziemy używać i jakie możliwości znajdą się w naszym programie. Projektowanie to przedewszystkim dokumentacja. Nie znaczy to że przed przystąpieniem do programowania powinniśmy napisać stertę papieru określając wszystkie API , klasy etc. Mam tu na myśli ogólny zarys aplikacji. Podział programu na warstwy logiczne (warstwy zajmujące się logicznie podzielonymi zadaniami, np. widok , kontroler , dostęp do danych, etc). Jeśli jesteśmy początkującymi programistami , nie wystarczy nam samo określenie warstw , i klas w nich się znajdujących. Potrzebujemy ok. 30% zarys programu. Dlaczego? Wielu z was pewnie zadaje sobie to pytanie. Pewnie też wielu z was zauważyło , że podczas pisania już gotowego kodu , dużo się zmienia od pierwowzoru który zaplanowaliście. Programując , napotykamy problem , który okazuje się nie możliwy do pokonania tak jak to wcześniej planowaliśmy. Zmieniamy nasz kod , i po kilku takich problemach, w nasz zarys aplikacji się sypie, późniejsze zmiany stają się dużo bardziej pracochłonne i trudne. Wiecie już , że projektowanie jest częścią pracy programisty. Jednakże , zawsze jest jakieś ale. Przy małych projektach wymagających kilka godzin pracy , które nie będą już rozwijane lub posłużą do jednorazowego użytku , trud i czas włożone w projektowanie nie zwrócą się.

Jak projektować?


Zakładając , że czytelnik ma zamiar napisać bardziej złożoną aplikacje i chce zaprojektować swój program mam zamiar przedstawić mu kilka prostych sposobów na przyśpieszenie tego procesu. Nie jest jednak możliwe określenie szczegółowego sposobu budowy każdej aplikacji. Można jedynie dać parę wskazówek. Warstwy , klasy , interfejsy , etc - OOP w praktyce. Nie znając podstaw OOP nie jesteśmy w stanie napisać bardziej złożonej aplikacji , która później będzie łatwo modyfikowalna. Dzięki programowaniu obiektowemu jesteśmy w stanie podzielić nasz kod na warstwy . To właśnie umiejętne podzielenie programu na warstwy logiczne jest kluczem do sukcesu. ?atwiej będzie sobie to wyobrazić na przykładzie prostego systemu CMS.Pisząc kod w którym pobieranie danych połączone jest z wyrzucaniem ich z gotowym kodem HTML jest widoczne w skrypcie JPORTAL , który uważany jest przez środowisko programistyczne , za jeden z najgorszych CMS-ów.

Wyobraźmy sobie budowę takiego programu:
Kod:
<html>
<head>
<title>Portal</title>
<!—inne nagłówki !-->
</head>
<body>
<div id="header">
<? Require_once('header.php')?>
</div>
<div id="menu">
<? Require_once('menu.php')?>
</div>
<div id="content">
<? Require_once('content.php')?>
</div>
<div id="footer">
<? Require_once('footer.php')?>
</div>
</body>
</html>


A teraz budowa przykladowego pliku content.php

Kod:
<?php

     $request = strtolower ($_GET['action']);

switch($request){
           case 'news':
                 $sql='SELECT * FROM `news` WHERE `status`=="active";';
                 while($line=mysql_fetch(mysql_query, MYSQL_ASSOC)){
                 ?><tr><td>tytuł: <?=$line->title?></td></tr><tr><td  ><?=$line->content?></td></tr>
                 }
                 Break;
     }
?>


To tylko uproszczony przykład, ale łatwo zauważyć , że drobna choć zmiana będzie wymagała tu dłużej pracy i głębokiej ingerencji w kod aplikacji. Przedstawie teraz przykład aplikacji napisanej za pomocą warstw logicznych i OOP. Aby nie wgłębiać się w budowe komponentów użyty zostanie ZEND FRAMEWORK, warstwą dostępu do bazy danych będzie phpDoctrine.

Plik NewsController.php

Kod:
<?php

class NewsController extends Zend_Controller_Action
{
   public function indexAction()
   {
       $post = Zend::registry('post');     
         $view = Zend::registry('view');
         
         
                  $news= new News;
               $newsTable=$news->findAll();
               $view->news= $newsTable;
               echo $view->render('news.php');
   }

}

?>


Plik news.php

Kod:
<!—wywolania innych szablonow odpowiedzialnych za nagłówki , menu etc !-->
<!--- czesc szablonu !-->
<?php foreach($this->news as $id -> $news){
?><tr><td>tytół: <?=$news->title?></td></tr><tr><td  ><?=$news->content?></td></tr>
<?
}
?>
<!—reszta szablonu !-->


Kod klasy News

Kod:
<?php
class News extends Doctrine_Record {
     
           
   public function setTableDefinition() {
           $this->hasColumn("id","integer",11,"primary|autoincreme  nt")
           $this->hasColumn("title","string",11)
           $this->hasColumn("content","string",230);
     }

}
?>


Wyobraźmy sobie teraz ze chcemy aby nasz news miał jeszcze datę utworzenia i autora;
W przykładzie pierwszym wymaga to całkowitej zmiany organizacji kodu. Dopisania relacji zmiany zapytań SQL i wiele innych. My natomiast ograniczamy się do dopisania paru linijek i klasy User analogicznej z klasa News.

[kod klasy news]

Kod:
<?php
class News extends Doctrine_Record {


public function setUp() {//dodane
       $this->hasOne("User as User", "News.user_id");

       
   }
     
           
   public function setTableDefinition() {
           $this->hasColumn("id","integer",11,"primary|autoincreme  nt")
           $this->hasColumn("title","string",11)
           $this->hasColumn("content","string",230);
           $this->hasColumn("created","integer",11);//dodane
$this->hasColumn("user_id","integer",11);//dodane


     }

}
?>


W kodzie szablonu dopisujemy tylko linijkę

Kod:
<tr><td>data utworzenia: <?=$news->created?></td></tr>


Same korzyści ?


Do zapoznania się z projektami phpDoctrine i Zend framework zapraszam na http://phpdoctrine.com/ oraz http://framework.zend.com/

W tych przykładach widzimy wyraźnie warstwę widoku (ZendView) warstwe kontrolera (NewsController) oraz warstwę danych którą jest phpDoctrine.

Co jeśli jednak nie piszemy aplikacji internetowych , w których nie będziemy używać wzorców MVC (model view controller). Tam także powinniśmy planować swoje aplikacje.

W tym przykładzie naszym zadaniem będzie stworzenie aplikacji odpowiedzialnej za zapisywanie do bazy faktur z możliwością przegladania ich.

Teraz , jak myślisz, na jakie warstwy trzeba podzielić naszą aplikacje?
To nie jest trudne pytanie. Dzielimy to co piszemy na klasy występujące w życiu. Fakturą będzie klasa Invoice , widok stworzymy sami , ktontroller mamy wbudowany w aplikacje która oparta jest na zdarzeniach , jednak , jeśli chcemy możemy napisać kontroler który będzie pośredniczył w wywoływaniu zdarzeń. Zwykłe onClick nie będzie już odnosić się do konkretnej funkcji lub metody. OnClick będzie teraz wywoływać metodę Controller::Run('NazwaAkcji',$parametry);
Napiszemy klasę Action posiadająca jedyną metodę Run. Oto "przekrój".

Kod:
<?php
Class Controller{

     Public static function Run($action,$parms){


     If class_exist($action){
           $action= new $action;
           $action->Run($action,$parms);
}
Return null //lub wyjatek
}

}
?>


Klasa Action

Kod:
<?php
Class Action{

     Public function Run(){
     }
}
?>


Klasa widoku.


Tutaj jednak nie będziemy projektować architektury która będzie dawała  nam nowe API do okienek które już posiadamy. Klasa widoku będzie udostępniać nam metody odpowiedzialne za widok już posiadamy. Będziemy mieli liste faktur którą będzie można odświeżać i filtrować, okienko dodawania faktury i inne rzeczy które przyjdą nam na myśl.

Kod:
<?php
Class View{

     Public static $handles= array() //tablica wskaźników do okienek

     Public static function refreshList(){
           //implementacja
     }

     Public static function updateList($parms){

     //implementacja      z czego $parms moze byc obiektem kolekcji faktur , lub tablica faktur, dowolnie
}

Public static function showAddInvoiceWin(){//implementacja
}

Public static function hideAddInvoiceWin(){//implementacja
}
//reszta kodu analogicznie.
}
?>


A co z dostępem do danych? Klasa Invoice powinna być podobna do modelu DAO z wszelkiego rodzaju getterami i setterami.

Co nam to da tak naprawdę?


Nie gubimy się w gąszczu naszych okienek , funkcji parametrów etc.
Wystarczy teraz ze klient zażyczy sobie aby można było edytować faktury.
My dopisujemy okienko z edycja , do widoku dodajemy parę funkcji, do klasy Invoice dodajemy metodęę Save(); i to wszystko.

Odkrywać Amerykę na nowo?


Jeśli jesteś ambitny , i uważasz że niektóre rzeczy napiszesz sam od nowa , to chwała ci! Jednak pomyśl , po co robić coś , co zrobiło już kilkadziesiąt osób? Czy nie lepiej poświęcić te kilka godzin na granie w piłkę , spotkania z przyjaciółmi etc , niż siedzieć przed komputerem? On ma przynosić ci pieniądze , i tylko pieniądze! Świetnie jeśli lubisz to co robisz , ale życie toczy się po za ekranem! Pamiętaj , dobry programista to leniwy programista! Masz problem? Szukaj gotowych rozwiązań, gotowych bibliotek!
Jeśli piszesz aplikacje z użyciem 10 swoich bibliotek , nie jesteś w stanie sprawdzać poprawnego działania każdej z nich! Niech robią to inni za ciebie!
Jeśli uważasz że niektóre wzorce nie spełniają swojego zastosowania to albo jesteś geniuszem , albo po prostu się mylisz. Tysiące programistów nie znalazło lepszego wyjścia z problemu. O co chodzi? Wzorce są po prostu sposobem na problem programistyczny. Tak jak Singelton zastępuje globalne , Collection tablice , tak i inne wzorce są po to by ułatwić ci pracę , a nie zmusić do ich używania. Oczywiście istnieje bardzo wiele wzorców bardzo do siebie podobnych , tak jak Visitor i Observer, jednak są podzielone na zastosowanie! To od twojego problemu zależy, jakich wzorców użyjesz, jak podzielisz aplikacje, i jak będzie działała.
To od Ciebie zależy ile czasu poświęcisz na dany kod i jakie będziesz miał z tego zyski. Liczy się czas twój a nie mikro sekundy które straci komputer na użyciu twojego kodu.

Koniec części pierwszej.

 

Źródło: HackingPlanet.org. Treść udostępniona na zasadach licencji Creative Commons Attribution

 

Autor: radmen

Licencja: Creative Commons