# Uzorci dizajna u web aplikacijama. Uzorci model-pogled-*

## Kompleksnost u razvoju softvera

Managing complexity is the most important technical topic in software development. In my view, it's so important that Software's Primary Technical Imperative has to be managing complexity.

Dijkstra pointed out [in 1972] that no one's skull is really big enough to contain a modern computer program, which means that we as software developers shouldn't try to cram whole programs into our skulls at once; we should try to organize our programs in such a way that we can safely focus on one part of it at a time. The goal is to minimize the amount of a program you have to think about at any one time.

Izvor: Steve McConnel, Code Complete, 2nd Edition, 2004.

## FizzBuzz

Napišite program koji ispisuje prvih 100 prirodnih brojeva tako da ispiše:

• Fizz ako je broj djeljiv s 3,
• Buzz ako je broj djeljiv s 5,
• FizzBuzz ako je broj djeljiv i s 3 i s 5,
• inače ispiše samo broj.

## Dizajn programa FizzBuzz (1/3)

for n in range(1, 101):
if n % 3 == 0 and n % 5 == 0:
print('FizzBuzz')
elif n % 3 == 0:
print('Fizz')
elif n % 5 == 0:
print('Buzz')
else:
print(n)


🙋 Pitanje: Je li ovo jedini pristup?

## Dizajn programa FizzBuzz (2/3)

for n in range(1, 101):
s = ''
if n % 3 == 0:
s = s + 'Fizz'
if n % 5 == 0:
s = s + 'Buzz'
if n % 5 != 0 and n % 3 != 0:
s = s + str(n)
print(s)


🙋 Pitanje: Postoji li još mogućnosti?

## Dizajn programa FizzBuzz (3/3)

public final class Main {
/**
* @param args
*/
public static void main(final String[] args) {
final ApplicationContext context = new
ClassPathXmlApplicationContext(Constants.SPRING_XML);
final FizzBuzz myFizzBuzz = (FizzBuzz)
context.getBean(Constants.STANDARD_FIZZ_BUZZ);
final FizzBuzzUpperLimitParameter fizzBuzzUpperLimit = new
DefaultFizzBuzzUpperLimitParameter();
myFizzBuzz.fizzBuzz(fizzBuzzUpperLimit.obtainUpperLimitValue());

((ConfigurableApplicationContext) context).close();
}
}


Čitav projekt na GitHubu: EnterpriseQualityCoding/FizzBuzzEnterpriseEdition

## Dizajn softvera

Prema Wikipediji: proces kojim agent (ovdje u značenju: programer s puno iskustva) stvara specifikaciju softverskog artefakta (npr. programa, okvira ili biblioteke) namijenjenog postizanju ciljeva, koristeći skup primitivnih komponenata i podložan ograničenjima; dvije mogućnosti:

• sve aktivnosti uključene u konceptualiziranje, uokvirivanje, implementaciju, puštanje u rad i konačno modificiranje složenih sustava

• aktivnost koja slijedi nakon specifikacije zahtjeva i prije programiranja, kao ... [u] stiliziranom procesu programskog inženjerstva

## Mjerila kvalitete dizajna softvera

Dobar dizajn softvera:

• maksimizira koherentnost: dijelovi softvera zajedno rade na logičan, razuman, lako uočljiv način
• minimizira sprezanje: dijelovi softvera se mogu koristiti odvojeno jedni od drugih, što specijalno olakšava njihovo ponovno korištenje

## Primjer ponovnog iskorištenje koda (1/2)

Želimo Pythonov objekt:

['foo', {'bar': ('baz', None, 1.0, 2)}]


pretvoriti u oblik JSON (Wikipedia):

["foo", {"bar": ["baz", null, 1.0, 2]}]


Moramo li pisati svoj kod za tu svrhu?

📝 Napomena: uočimo da Pythonovi i JSON-ovi tipovi podataka nisu isti.

## Primjer ponovnog iskorištenje koda (2/2)

Pythonov modul json omogućuje ponovnu iskoristivost funkcija za rad s JSON-om (npr. pretvorba u, pretvorba iz):

import json

o = ['foo', {'bar': ('baz', None, 1.0, 2)}]
j = json.dumps(o)
# j će imati vrijednost '["foo", {"bar": ["baz", null, 1.0, 2]}]'


🙋 Pitanje: Možemo li dizajn softvera ponovno iskoristiti kao što ponovno iskorištavamo kod?

## Ponovno iskorištenje dizajna i uzorci dizajna

Prema Wikipediji:

• opće, ponovno iskoristivo rješenje za problem koji se često javlja kod dizajna softvera; nije gotov dizajn koji se može odmah prevesti u izvorni kod
• opis postupka ili predložak za rješavanje problema koji se može koristiti u različitim situacijama
• formalizirane najbolje prakse koje programer može koristiti za rješavanje uobičajenih problema prilikom dizajniranja aplikacije ili sustava

## Početak uzoraka dizajna

• Gang of Four (GoF), Design Patterns book, 1994.
• kritika (Paul Graham i Peter Norvig): uzorci dizajna služe za zaobilaženje nedostataka C++-a
• mogu se za istu svrhu iskoristiti makroi (predprocesorska naredba #define)
• funkcijski jezici kao Lisp nemaju potrebu za većinom uzoraka

## Uzorci dizajna - birokracija

Izvor: Design Patterns - Bureaucracy (MonkeyUser, 26th September 2017)

## Kreacijski uzorci (engl. creational patterns)

• (C) Apstraktna tvornica (engl. abstract factory)
• (C) Tvornica (engl. factory)
• (C) Prototip (engl. prototype)
• (C) Singleton

## Strukturni uzorci (engl. structural patterns)

• (S) Most (engl. bridge)
• (S) Smjesa (engl. composite)
• (S) Dekorator (engl. decorator)
• (S) Muhavac (engl. flyweight)
• (S) Opunomoćenik (engl. proxy)

## Uzorci ponašanja (engl. behavioral patterns)

• (B) Lanac odgovornosti (engl. chain of responsibility)
• (B) Naredba (engl. command)
• (B) Interpreter
• (B) Iterator
• (B) Posrednik (engl. mediator)
• (B) Uspomena (engl. memento)
• (B) Promatrač (engl. observer)
• (B) Stanje (engl. state)
• (B) Strategija (engl. strategy)
• (B) Predložak (engl. template)
• (B) Posjetitelj (engl. visitor)

## Pregled svih 23 uzoraka dizajna prema GoF

Izvor: Design Patterns Quick Reference, autor Jason McDonald (2007.)

## Tvornica (1/5)

<?php

class Motor {
// ...
}

class MotorFactory {
public function create() : Motor {
$motor = new Motor(); return$motor;
}
}

$motorFactory = MotorFactory();$motor = $motorFactory->create();  ## Tvornica (2/5) <?php class Motor {} class GasolineMotor extends Motor {} class DieselMotor extends Motor {} class ElectricMotor extends Motor {} class Car { public Motor$motor;

public function __construct() {
$motor = new Motor(); } }  🙋 Pitanje: Što ćemo s različitim vrstama motora? ## Tvornica (3/5) <?php class Car {} class DieselCar extends Car { public Motor$motor;

public function __construct() {
$motor = new DieselMotor(); } } class GasolineCar extends Car { // analogno }  🙋 Pitanje: Možemo li napraviti bolji dizajn od ovog? ## Tvornica (4/5) <?php class MotorFactory { string$type;

public function create() : Motor {
$motor = new$type();
return $motor; } }$dieselMotorFactory = MotorFactory();
$dieselMotorFactory->type = "DieselMotor";  ## Tvornica (5/5) <?php class Car { public Motor$motor;

public function __construct(MotorFactory $factory) {$motor = $factory->create(); } }$car = new Car($dieselMotorFactory);$electricMotorFactory = MotorFactory();
$electricMotorFactory->type = "ElectricMotor";$bmwConceptI4 = new Car($electricMotorFactory);  ## Apstraktna tvornica (1/2) class Game attr_accessor :title def initialize(title) @title = title end end class Rpg < Game def description puts "I am a RPG named #{@title}" end end class Arcade < Game def description puts "I am an Arcade named #{@title}" end end  ## Apstraktna tvornica (2/2) class GameFactory def create(title) raise NotImplementedError, "You should implement this method" end end class RpgFactory < GameFactory def create(title) Rpg.new title end end class ArcadeFactory < GameFactory def create(title) Arcade.new title end end  ## Graditelj # class Builder class UserBuilder(Builder): def __init__(self): self._user_ = User() def user(self): user = self._user_ self._user_ = User() return user def facebook_connection(self): self._user_.add_connection("Facebook") def google_connection(self): self._user_.add_connection("Google") def github_connection(self): self._user_.add_connection("Github")  ## Prototip (1/2) <?php class BlogArticle { private$title;
private $body; private$author;
private $date; private$comments = [];

public function __construct(string $title, string$body,
Author $author) {$this->title = $title;$this->body = $body;$this->author = $author;$this->date = new \DateTime();
}
}


## Prototip (2/2)

<?php

class BlogArticle
{
// ...

public function __clone()
{
$this->title = "Copy of " .$this->title;
$this->date = new \DateTime();$this->comments = [];
}
}


## Singleton

class Singleton:
__instance = None

@staticmethod
def getInstance():
if Singleton.__instance == None:
Singleton()
return Singleton.__instance

def __init__(self):
if Singleton.__instance != None:
raise Exception("This class is a singleton!")
else:
Singleton.__instance = self

s1 = Singleton()
s2 = Singleton.getInstance()

# s1 == s2


class WeatherForecast:
def getTemperature(self, location, date_time):
# returns F

def getWindSpeed(self, location, date_time):
# returns mph

__forecast = None

def __init__(self, forecast):
self.__forecast = forecast

def getTermperature(self, location, date_time):
return (forecast.getTemperature(location, date_time) - 32) / 1.8

def getWindSpeed(self, location, date_time):
return forecast.getWindSpeed(location, date_time) * 1.609344


## Most (1/5)

<?php

abstract class Article
{
protected $renderer; public function __construct(Renderer$renderer)
{
$this->renderer =$renderer;
}

public function changeRenderer(Renderer $renderer): void {$this->renderer = $renderer; } abstract public function view(): string; }  ## Most (2/5) <?php class Letter extends Article { // ... public function view(): string { return$this->renderer->renderParts( /* ... */ );
}
}

class JournalArticle extends Article
{
// ...
public function view(): string
{
return $this->renderer->renderParts( /* ... */ ); } }  ## Most (3/5) <?php interface Renderer { public function renderTitle(string$title): string;

public function renderTextBlock(string $text): string; public function renderImage(string$url): string;

public function renderLink(string $url, string$title): string;

public function renderFooter(): string;

public function renderParts(array $parts): string; }  ## Most (4/5) <?php class HTMLRenderer implements Renderer { public function renderTitle(string$title): string
{
return "<h1>$title</h1>"; } public function renderTextBlock(string$text): string
{
return "<p>$text</p"; } // ... } class PDFRenderer implements Renderer { /* ... */ }  ## Most (5/5) <?php class JSONRenderer implements Renderer { // ... } class XMLRenderer implements Renderer { // ... }  ## Dekorator (1/3) <?php interface OpenerInterface { public function open() : void; } class Door implements OpenerInterface { public function open() : void { // opens the door } } class Window implements OpenerInterface { public function open() : void { // opens the window } }  ## Dekorator (2/3) <?php class SmartDoor extends Door { public function open() : void { parent::open();$this->temperature();
}
}

class SmartWindow extends Window {
public function open() : void {
parent::open();
$this->temperature(); } }  🙋 Pitanje: Moramo li ponavljati nasljeđivanje za svaki pametni uređaj? ## Dekorator (3/3) <?php class SmartOpener implements OpenerInterface { private$opener;

public function __construct(OpenerInterface $opener) {$this->opener = $opener; } public function open() : void {$this->opener->open();
$this->temperature(); } }$door = new Door();
$window = new Window();$smartDoor = new SmartOpener($door);$smartWindow = new SmartOpener($window);  ## Fasada (1/2) class DatabaseConnection: def query(self, data) -> str: # ... class CacheConnection: def is_available(self, data) -> str: # ... def get(self, data) -> str: # ...  ### Fasada (2/2) class Facade: def __init__(self, databaseConnection: DatabaseConnection, cacheConnection: CacheConnection) -> None: self._databaseConnection = databaseConnection or DatabaseConnection() self._cacheConnection = cacheConnection or CacheConnection() def operation(self, data) -> str: if self._cacheConnection.is_available(data): return self._cacheConnection.get(data) else return self._databaseConnection.query(data) if __name__ == "__main__": databaseConnection = DatabaseConnection() cacheConnection = CacheConnection() facade = Facade(databaseConnection, cacheConnection) # data ... facade.get(data)  ## Opunomoćenik (1/3) <?php interface DataRetriever { public function retrieve(string$data): string;
}

class DatabaseRetriever implements DataRetriever
{
private $dbConnection; public function retrieve(string$data): string
{
return $dbConnection->query($data);
}
}


## Opunomoćenik (2/3)

<?php

class CachingRetriever implements DataRetriever
{
private $databaseRetriever; private$cache = [];

public function __construct(DatabaseRetriever $databaseRetriever) {$this->databaseRetriever = $databaseRetriever; } }  ## Opunomoćenik (3/3) <?php class CachingRetriever implements DataRetriever { // ... public function retrieve(string$data): string
{
if (isset($this->cache[$data])) {
return $this->cache[$url];
}

$result =$this->databaseRetriever->retrieve($url);$this->cache[$url] =$result;
return $result; } }  ## Lanac odgovornosti (1/2) class Handler: _next_handler = None def set_next(self, handler): self._next_handler = handler return handler def handle(self, request): if self._next_handler: return self._next_handler.handle(request) return None  ## Lanac odgovornosti (2/2) class FTPDownloader(Handler): def handle(self, request): if request.startswith("ftp://"): # ... else: return super().handle(request) class HTTPDownloader(Handler): def handle(self, request) -> str: if request.startswith("http://") or request.startswith("https://"): # ... else: return super().handle(request)  ## Naredba import { exec } from 'child_process'; interface Command { execute(): void; } class ImageMagickCommand implements Command { private parameters; constructor(parameters) { this.parameters = parameters; } execute() { exec('imagemagick ' + parameters, (err, stdout, stderr) => { /* ... */ }); } } myCommand = ImageMagickCommand('-crop 32x32+16+16 image.png');  ## Interpreter exec("""for i in range(10):\n print('Hello world!')""") result = eval("5 + 2 * a")  ## Iterator (1/2) class Iterator attr_accessor :reverse private :reverse attr_accessor :collection private :collection def initialize(collection, reverse = false) @collection = collection @reverse = reverse end def each(&block) return @collection.reverse.each(&block) if reverse @collection.each(&block) end end  ## Iterator (2/2) class ArticlesCollection attr_accessor :collection private :collection def initialize(collection = []) @collection = collection end def iterator Iterator.new(@collection) end def reverse_iterator Iterator.new(@collection, true) end def add_item(item) @collection << item end end  ## Posrednik (1/2) interface ArticleRetriever { getArticle(id: number): void; } class DatabaseArticleRetriever implements ArticleRetriever { public getArticle(id: number): void { /* ... */ } }  ## Posrednik (2/2) class Proxy implements ArticleRetriever { private articleRetriever: ArticleRetriever; constructor(articleRetriever: ArticleRetriever) { this.articleRetriever = articleRetriever; } public getArticle(id: number): void { if (this.isValid(id)) { this.realSubject.request(id); } } private isValid(id: number): boolean { /* ... */ } }  ## Promatrač function Order() { this.observers = []; } Order.prototype = { subscribe: function(fn) { this.observers.push(fn); }, unsubscribe: function(fn) { this.observers = this.observers.filter( function(item) { if (item !== fn) { return item; }}); }, fire: function() { this.observers.forEach( function(fn) { fn.call(); }); } }  ## Stanje <?php class Ad { private string$state;

public function publish() {
switch ($state) { case "draft":$state = "moderation";
break;
case "moderation":
if ($currentUser.isModerator()) {$state = "published";
}
break;
case "published":
break;
}
}


## Strategija

class MapRouter
def do_routing(start, end)
raise NotImplementedError,
"#{self.class} has not implemented method '#{__method__}'"
end
end

class OSRMMapRouter < MapRouter
def do_routing(start, end)
# ...
end
end

class GraphHopperMapRouter < MapRouter
def do_routing(start, end)
# ...
end
end


## Primjeri primjene uzoraka dizajna

🙋 Pitanje: Koje uzorke dizajna koriste navedeni programi, okviri i biblioteke?

## Model, pogled i upravitelj (1/3)

Engl. model-view-controller, kraće MVC. Prema Wikipediji:

• uzorak softverskog dizajna koji se obično koristi za razvoj grafičkih korisničkih sučelja
• dijeli povezanu programsku logiku na tri međusobno povezana elementa
• odvaja unutarnji prikaz informacija od načina na koji se informacije prezentiraju korisniku i prihvaćaju korisnički unosi

## Model, pogled i upravitelj (2/3)

Koristi se kod razvoja:

## Model, pogled i upravitelj (3/3)

Prema Wikipediji:

• Model je središnja komponenta uzorka, neovisna o korisničkom sučelju
• Pogled je bilo koji prikaz informacija kao što su grafikon, dijagram ili tablica; mogući su višestruki prikazi istih podataka
• Kontroler prihvaća ulaz i pretvara ga u naredbe za model ili pogled

Interakcija komponenata:

• Model upravlja podacima aplikacije. Prima korisnički unos od kontrolera.
• Pogled prikazuje prezentaciju modela u određenom obliku.
• Kontroler reagira na korisnički unos i interagira s objektima podatkovnog modela. Kontroler prima ulaz, po želji ga potvrđuje i zatim prosljeđuje ulaz modelu.

Engl. model-view-adapter, kraće MVA. Prema Wikipediji:

• za razliku od MVC-a, spaja linearno model i pogled putem adaptera
• model ne komunicira s pogledom osim preko adaptera
• model ne zna koji pogledi postoje
• pogled ne zna koji modeli postoje

## Model, pogled i prezenter

Engl. model-view-presenter, kraće MVP. Prema Wikipediji:

• MVP je derivat MVC-a, prezenter zamjenuje kontroler
• pogled je pasivan element
• prezenter sadrži svu prezentacijsku logiku

## Model, pogled i pogled modela

Engl. model-view-viewmodel, kraće MVVM. Prema Wikipediji:

• pogled modela je sličan kao prezenter, ali ne koristi pogled izravno
• pogled koristi osvježava svojstva pogleda modela korištenjem bindera

## MVC, MVA, MVP i MVVM

• u suštini vrlo slični, razlike među njima nisu velike
• lako je prijeći iz jednog od pristupa u drugi
• odabir pristupa uglavnom vršimo odabirom okvira u kojem radimo
• vještina rada u jednom MVC okviru je prenosiva u drugi MVC okvir, npr. iz ASP.NET MVC u Ruby on Rails ili Laravel

## Primjena uzoraka dizajna u razvoju softvera

Uzorci dizajna koriste se po potrebi. Primjerice:

• okvir u kojem je vaša aplikacija napravljena može biti MVC,
• možete iskoristiti adapter za pozivanje neke biblioteke čiji API ne paše,
• za stvaranje objekata na temelju modela možete koristiti apstraktnu tvornicu
• u kontroleru možete iskoristiti promatrača u radu s modelima
• itd.

Author: Vedran Miletić