Interfejs: Różnice pomiędzy wersjami

Z PHPEdia.pl
Skocz do: nawigacji, wyszukiwania
(Dopisałem zalążek :D)
 
m (Przykład zastosowania: uszkodzony nagłówek)
 
(Nie pokazano 8 wersji utworzonych przez 6 użytkowników)
Linia 1: Linia 1:
W PHP 5 dostajemy do naszych śapek także nieocenione interfejsy, umożliwiające w łatwy sposób poprawią czytelnośc rozbudowanych klas korzystających z tych samych szablonów funkcji, przyjmżjących różne działałnie w różnych klasach, mówiąc krżtko komantarze piszemy tylko raz i kod niestraci na czytelności.
+
Interfejs jest czymś na kształt wzorca dla [[klasa|klas]]: wymusza żeby klasy, które go implementują zawierały zdefiniowane w nim [[metody]]. Interfejs definiuje jedynie, jakie [[metoda|metody]] "dostępne z zewnątrz" ([[public|publiczne]]) [[klasa]] musi zawierać - bez podania ich wewnętrznej budowy i metod prywatnych.
Niech słowo stanie sie ciałem, prosty przykład.
+
 
 +
==Dostępność==
 +
Interfejsy zostały wprowadzone od [[PHP_5|PHP 5.0]].
 +
 
 +
==Definiowanie interfejsu==
 +
 
 +
Interfejs definiujemy podobnie, jak zwykłą klasę, ale z użyciem słowa kluczowego <tt>interface</tt>, zamiast <tt>class</tt>.
 +
Definicję wymaganych metod kończymy średnikiem (<tt>;</tt>), bez podawania żadnej zawartości.
 +
Wszystkie metody interfejsu muszą być publiczne, wynika to z natury interfejsu - ma definiować jedynie coś na kształt dostępnego z zewnątrz API klas.
 +
 
 
<source lang="php">
 
<source lang="php">
 
<?php
 
<?php
interface silnik //Definiujemy interfejs o nazwie silnik
+
 
 +
/**
 +
* Definicja interfejsu
 +
*/
 +
interface Testowy
 
{
 
{
  public function Zapal(); //Funkcja zapala silnik
+
/**
  public function Zatrzymaj(); //Funkcja gasi silnik
+
* Metoda, którą musi zawierać klasa opierająca się na tym interfejsie
 +
*/
 +
public function wymaganaMetoda($argument, $innyArgument);
 +
 
 +
/**
 +
* Inna wymagana metoda
 +
*/
 +
public function innaWymaganaMetoda($argument);
 +
 
 +
/*
 +
* ...
 +
* Definicje innych wymaganych metod
 +
* ...
 +
*/
 
}
 
}
 +
?>
 +
</source>
 +
 +
== Klasa implementująca interfejs ==
 +
 +
Klasę, która ma opierać się na interfejsie, definiujemy podobnie do normalnej - z tym, że po nazwie klasy musimy zastosować słówko <tt>implements</tt> i nazwę interfejsu.
 +
 +
<source lang="php">
 +
<?php
  
class peugot implements silnik
+
/**
 +
* Klasa, która implementuje interfejs Testowy
 +
*/
 +
class Klasa implements Testowy
 
{
 
{
  public function Zapal()
+
/*
  {
+
* Zwykła definicja klasy, w której muszą się znależą metody zdefiniowane w interfejsie Testowy
      echo( 'brum brum, peugot zapalil' );
+
*/
  }
+
 +
/**
 +
* Metoda wymagana przez interfejs
 +
*/
 +
public function wymaganaMetoda($argument, $innyArgument)
 +
{
 +
/*
 +
* ...
 +
* Zawartość metody
 +
* ...
 +
*/
 +
}
  
  public function Zatrzymaj()
+
  {
+
/**
      echo( 'pffff, peugot zgasl' );
+
* Metoda wymagana przez interfejs
  }
+
*/
 +
public function wymaganaMetoda($argument, $innyArgument)
 +
{
 +
/*
 +
* ...
 +
* Zawartość metody
 +
* ...
 +
*/
 +
}
 +
 
 +
/*
 +
* ...
 +
* Inne metody, które nie są wymagane - publiczne lub prywatne
 +
* ...
 +
*/
 
}
 
}
  
class mercedes implements silnik
+
?>
 +
</source>
 +
 
 +
Klasa może implementować kilka interfejsów - wtedy musi zawierać metody zdefiniowane w każdym z nich. W takim przypadku interfejsy wymieniamy po przecinku:
 +
 
 +
<source lang="php">
 +
<?php
 +
 
 +
/**
 +
* Klasa, która implementuje interfejsy Testowy i InnyInterfejs
 +
*/
 +
class Klasa implements Testowy, InnyInterfejs
 
{
 
{
  public function Zapal()
+
/*
  {
+
* ...
      echo( 'brum brum, mercedes zapalil' );
+
* Metody zdefiniowane w interfejsach Testowy i InnyInterfejs
  }
+
* ...
 +
*/
 +
}
  
  public function Zatrzymaj()
+
?>
  {
+
</source>
      echo( 'pffff, mercedes zgasl' );
+
 
  }
+
Gdy klasa nie będzie zawierała wszystkich zdefiniowanych w interfejsach metod - będzie to skutkowało błędem krytycznym, a więc zatrzymaniem wykonywania skryptu.
 +
 
 +
==Kiedy warto je stosować?==
 +
Zawsze wtedy, gdy potrzebujemy uniwersalnego wzorca dla kilku klas, lub podejrzewamy, że w przyszłości będziemy chcieli wymienić daną klasę na inną (np. korzystając z innego źródła danych), bez potrzeby modyfikowania reszty aplikacji.
 +
 
 +
==Wbudowane interfejsy==
 +
PHP, począwszy od wersji [[PHP_5|5.0]], ma standardowo wbudowane pewne interfejsy - np. te znajdujące się w [[SPL|Standardowej Bibliotece PHP]]. Są to głównie [[iterator]]y.
 +
 
 +
==Przykład zastosowania==
 +
===Parser adresów URL===
 +
Czasami chcemy zastosować w aplikacji URLe w innej niż standardowa (<tt>plik.php?argument1=10&argument2=test</tt>)postaci, np: <tt>plik.php/argument1.10/argument2.test</tt>. Wystarczy napisać zwykśy parser, w postaci prostej klasy. Co jednak zrobić, gdy będziemy chcieli kiedyś zmienić postać URLa, np. wrócić do tej standardowej?
 +
 
 +
Najlepszym rozwiązaniem jest napisanie wtedy nowej klasy do parsowania URLa, jednak z takimi samymi dostępnymi metodami publicznymi - abyśmy nie musieli przebudowywać całej aplikacji.
 +
 
 +
Tutaj z pomocą przychodzć właśnie interfejsy - już na etapie planowania aplikacji ustalamy, jakie funkcje publiczne (API) będą nam potrzebne, jakie argumenty powinny one przyjmować oraz jakie wartości zwracać. Definiujemy to wszystko tworząc interfejs, np. taki:
 +
 
 +
<source lang="php">
 +
<?php
 +
/*
 +
* Kod zadziałał jedynie w PHP od wersji 5.0
 +
*
 +
* Komentarze w formacie phpDoc
 +
*/
 +
 
 +
/**
 +
* Interfejs dla parsera URL
 +
*/
 +
interface Url
 +
{
 +
/**
 +
* Konstruktor
 +
*
 +
* @param string $url URL do sparsowania
 +
* @return void
 +
*/
 +
public function __construct($url);
 +
 +
/**
 +
* Zwraca parametr wyciągnięty z URLa
 +
*
 +
* @param string $name Nazwa parametru
 +
* @return string Wartość parametru
 +
*/
 +
public function getParam($name);
 +
 +
/**
 +
* Zwraca ścieżkę wyciągniętą z URLa
 +
*
 +
* @return string Scieżka
 +
*/
 +
public function getPath();
 +
 +
/**
 +
* Zwraca nazwę hosta wyciągniętą z URLa
 +
*
 +
* @return string Nazwa hosta
 +
*/
 +
public function getHost();
 +
 +
/**
 +
* Zwraca prefiks protokołu wyciągnięty z adresu URL
 +
*
 +
* @return string Prefiks protokołu
 +
*/
 +
public function getProtocol();
 
}
 
}
  
$peugot = new peugot;
+
?>
$peugot->Zapal();
+
</source>
$peugot->Zatrzymaj();
+
 
 +
Teraz wystarczy, że napiszemy klasą parsującć URL, która implementuje interfejs <tt>Url</tt>:
 +
 
 +
<source lang="php">
 +
<?php
 +
/*
 +
* Kod zadziałał jedynie w PHP od wersji 5.0
 +
*
 +
* Komentarze w formacie phpDoc
 +
*/
 +
 
 +
/**
 +
* Parser URLa w standardowym formacie
 +
*/
 +
class StandardUrl implements Url
 +
{
 +
/**
 +
* @var string Niesparsowany URL
 +
*/
 +
private $url;
 +
 
 +
/**
 +
* @var array Parametry wyciągnićte z URLa
 +
*/
 +
private $params;
 +
 
 +
/**
 +
* @var string Nazwa hosta wyciągnićta z URLa
 +
*/
 +
private $hostName;
 +
 
 +
/**
 +
* @var string Ścieżka  wyciągnięta z URLa
 +
*/
 +
private $path;
 +
 
 +
/**
 +
* Konstruktor
 +
*
 +
* @param string $url URL do sparsowania
 +
* @return void
 +
*/
 +
public function __construct($url)
 +
{
 +
$this->url = $url;
 +
 +
try
 +
{
 +
$this->parse();
 +
}
 +
catch (Exception $e)
 +
{
 +
// ... obsługa błędów ...
 +
}
 +
}
 +
 +
/**
 +
* Parsuje URL
 +
*
 +
* @return void
 +
*/
 +
private function parse()
 +
{
 +
/*
 +
* ...
 +
* Parsowanie URLa:
 +
* Wyciąganie z niego parametrów, nazwy hosta, ścieżki, prefiksu protokołu, itp
 +
* oraz wwalenie ich do odpowiednich pól klasy
 +
* ...
 +
*/
 +
}
 +
 +
/**
 +
* Zwraca parametr wyciągnićty z URLa
 +
*
 +
* @param string $name Nazwa parametru
 +
* @return string Wartość parametru
 +
*/
 +
public function getParam($name)
 +
{
 +
if (isset ($this->params[$name]))
 +
{
 +
return $this->params[$name];
 +
}
 +
else
 +
{
 +
throw new Exception('Parametr nie istnieje');
 +
}
 +
}
 +
 +
/**
 +
* Zwraca ścieżkć wyciągniętą z URLa
 +
*
 +
* @return string Ä?Âącieżka
 +
*/
 +
public function getPath()
 +
{
 +
if ($this->path)
 +
{
 +
return $this->path;
 +
}
 +
else
 +
{
 +
throw new Exception('Sciezka nie zostala zdefiniowana');
 +
}
 +
}
 +
 +
/**
 +
* Zwraca nazwę hosta wyciągniętą z URLa
 +
*
 +
* @return string Nazwa hosta
 +
*/
 +
public function getHostName()
 +
{
 +
if ($this->hostName)
 +
{
 +
return $this->hostName;
 +
}
 +
else
 +
{
 +
throw new Exception('Nazwa hosta nie zostala zdefiniowana');
 +
}
 +
}
 +
}
  
$mercedes = new mercedes;
 
$mercedes->Zapal();
 
$mercedes->Zatrzymaj();
 
 
?>
 
?>
 
</source>
 
</source>
Ten przykład powinien wszystko rozjasnic i rzucic w jasniejszym swietle te jakze przyjazne bestie zwane interfejsami.
+
 
{{stub}}
+
oprócz metod wymaganych przez interfejs <tt>Url</tt>, klasa zawiera również metodą <tt>parse()</tt>, która jest wykorzystywana do parsowania URLa.
 +
 
 +
Jeśli chcemy zmienić format URLa - wystarczy, że napiszemy nową klasą opartą na interfejsie <tt>Url</tt>. Struktura wewnątrzna i proces parsowania może być całkiem inny - ważne, żeby klasa zawierała metody zdefiniowane w interfejsie. dzięki temu mamy gwarancję, że taka zmiana nie wpłynie na resztą aplikacji, a ewentualne błędy w implementacji interfejsu <tt>Url</tt> zostaną wykryte przy uruchomieniu aplikacji.
 +
 
 +
==Więcej informacji==
 +
[http://docs.php.net/en/language.oop5.interfaces.html Manual PHP.net]
 +
[[Kategoria:OOP]][[Kategoria:PHP 5]]

Aktualna wersja na dzień 17:17, 19 mar 2009

Interfejs jest czymś na kształt wzorca dla klas: wymusza żeby klasy, które go implementują zawierały zdefiniowane w nim metody. Interfejs definiuje jedynie, jakie metody "dostępne z zewnątrz" (publiczne) klasa musi zawierać - bez podania ich wewnętrznej budowy i metod prywatnych.

Dostępność

Interfejsy zostały wprowadzone od PHP 5.0.

Definiowanie interfejsu

Interfejs definiujemy podobnie, jak zwykłą klasę, ale z użyciem słowa kluczowego interface, zamiast class. Definicję wymaganych metod kończymy średnikiem (;), bez podawania żadnej zawartości. Wszystkie metody interfejsu muszą być publiczne, wynika to z natury interfejsu - ma definiować jedynie coś na kształt dostępnego z zewnątrz API klas.

<?php
 
/**
* Definicja interfejsu
*/
interface Testowy
{
	/**
	* Metoda, którą musi zawierać klasa opierająca się na tym interfejsie
	*/
	public function wymaganaMetoda($argument, $innyArgument);
 
	/**
	* Inna wymagana metoda
	*/
	public function innaWymaganaMetoda($argument);
 
	/*
	* ...
	* Definicje innych wymaganych metod
	* ...
	*/
}
?>

Klasa implementująca interfejs

Klasę, która ma opierać się na interfejsie, definiujemy podobnie do normalnej - z tym, że po nazwie klasy musimy zastosować słówko implements i nazwę interfejsu.

<?php
 
/**
* Klasa, która implementuje interfejs Testowy
*/
class Klasa implements Testowy
{
	/*
	* Zwykła definicja klasy, w której muszą się znależą metody zdefiniowane w interfejsie Testowy
	*/
 
	/**
	* Metoda wymagana przez interfejs
	*/
	public function wymaganaMetoda($argument, $innyArgument)
	{
		/*
		* ...
		* Zawartość metody
		* ...
		*/
	}
 
 
	/**
	* Metoda wymagana przez interfejs
	*/
	public function wymaganaMetoda($argument, $innyArgument)
	{
		/*
		* ...
		* Zawartość metody
		* ...
		*/
	}
 
	/*
	* ...
	* Inne metody, które nie są wymagane - publiczne lub prywatne
	* ...
	*/
}
 
?>

Klasa może implementować kilka interfejsów - wtedy musi zawierać metody zdefiniowane w każdym z nich. W takim przypadku interfejsy wymieniamy po przecinku:

<?php
 
/**
* Klasa, która implementuje interfejsy Testowy i InnyInterfejs
*/
class Klasa implements Testowy, InnyInterfejs
{
	/*
	* ...
	* Metody zdefiniowane w interfejsach Testowy i InnyInterfejs
	* ...
	*/	
}
 
?>

Gdy klasa nie będzie zawierała wszystkich zdefiniowanych w interfejsach metod - będzie to skutkowało błędem krytycznym, a więc zatrzymaniem wykonywania skryptu.

Kiedy warto je stosować?

Zawsze wtedy, gdy potrzebujemy uniwersalnego wzorca dla kilku klas, lub podejrzewamy, że w przyszłości będziemy chcieli wymienić daną klasę na inną (np. korzystając z innego źródła danych), bez potrzeby modyfikowania reszty aplikacji.

Wbudowane interfejsy

PHP, począwszy od wersji 5.0, ma standardowo wbudowane pewne interfejsy - np. te znajdujące się w Standardowej Bibliotece PHP. Są to głównie iteratory.

Przykład zastosowania

Parser adresów URL

Czasami chcemy zastosować w aplikacji URLe w innej niż standardowa (plik.php?argument1=10&argument2=test)postaci, np: plik.php/argument1.10/argument2.test. Wystarczy napisać zwykśy parser, w postaci prostej klasy. Co jednak zrobić, gdy będziemy chcieli kiedyś zmienić postać URLa, np. wrócić do tej standardowej?

Najlepszym rozwiązaniem jest napisanie wtedy nowej klasy do parsowania URLa, jednak z takimi samymi dostępnymi metodami publicznymi - abyśmy nie musieli przebudowywać całej aplikacji.

Tutaj z pomocą przychodzć właśnie interfejsy - już na etapie planowania aplikacji ustalamy, jakie funkcje publiczne (API) będą nam potrzebne, jakie argumenty powinny one przyjmować oraz jakie wartości zwracać. Definiujemy to wszystko tworząc interfejs, np. taki:

<?php
/*
* Kod zadziałał jedynie w PHP od wersji 5.0
*
* Komentarze w formacie phpDoc
*/
 
/**
* Interfejs dla parsera URL
*/
interface Url
{
	/**
	* Konstruktor
	*
	* @param	string	$url	URL do sparsowania
	* @return	void
	*/
	public function __construct($url);
 
	/**
	* Zwraca parametr wyciągnięty z URLa
	*
	* @param	string	$name	Nazwa parametru
	* @return	string		Wartość parametru
	*/
	public function getParam($name);
 
	/**
	* Zwraca ścieżkę wyciągniętą z URLa
	*
	* @return	string		Scieżka
	*/
	public function getPath();
 
	/**
	* Zwraca nazwę hosta wyciągniętą z URLa
	*
	* @return	string		Nazwa hosta
	*/
	public function getHost();
 
	/**
	* Zwraca prefiks protokołu wyciągnięty z adresu URL
	*
	* @return	string		Prefiks protokołu
	*/
	public function getProtocol();
}
 
?>

Teraz wystarczy, że napiszemy klasą parsującć URL, która implementuje interfejs Url:

<?php
/*
* Kod zadziałał jedynie w PHP od wersji 5.0
*
* Komentarze w formacie phpDoc
*/
 
/**
* Parser URLa w standardowym formacie
*/
class StandardUrl implements Url
{
	/**
	* @var	string	Niesparsowany URL
	*/
	private $url;	
 
	/**
	* @var	array	Parametry wyciągnićte z URLa
	*/
	private $params;
 
	/**
	* @var	string	Nazwa hosta wyciągnićta z URLa
	*/
	private $hostName;
 
	/**
	* @var	string	Ścieżka  wyciągnięta z URLa
	*/
	private $path;
 
	/**
	* Konstruktor
	*
	* @param	string	$url	URL do sparsowania
	* @return	void
	*/
	public function __construct($url)
	{
		$this->url = $url;
 
		try
		{
			$this->parse();
		}
		catch (Exception $e)
		{
			// ... obsługa błędów ...	
		}
	}
 
	/**
	* Parsuje URL
	*
	* @return void
	*/
	private function parse()
	{
		/*
		* ...
		* Parsowanie URLa:
		* Wyciąganie z niego parametrów, nazwy hosta, ścieżki, prefiksu protokołu, itp
		* oraz wwalenie ich do odpowiednich pól klasy
		* ...
		*/
	}
 
	/**
	* Zwraca parametr wyciągnićty z URLa
	*
	* @param	string	$name	Nazwa parametru
	* @return	string			Wartość parametru
	*/
	public function getParam($name)
	{
		if (isset ($this->params[$name]))
		{
			return $this->params[$name];
		}
		else 
		{
			throw new Exception('Parametr nie istnieje');
		}
	}
 
	/**
	* Zwraca ścieżkć wyciągniętą z URLa
	*
	* @return	string		Ä?Âącieżka
	*/
	public function getPath()
	{
		if ($this->path)
		{
			return $this->path;
		}
		else 
		{
			throw new Exception('Sciezka nie zostala zdefiniowana');
		}
	}
 
	/**
	* Zwraca nazwę hosta wyciągniętą z URLa
	*
	* @return	string		Nazwa hosta
	*/
	public function getHostName()
	{
		if ($this->hostName)
		{
			return $this->hostName;
		}
		else 
		{
			throw new Exception('Nazwa hosta nie zostala zdefiniowana');
		}
	}
}
 
?>

oprócz metod wymaganych przez interfejs Url, klasa zawiera również metodą parse(), która jest wykorzystywana do parsowania URLa.

Jeśli chcemy zmienić format URLa - wystarczy, że napiszemy nową klasą opartą na interfejsie Url. Struktura wewnątrzna i proces parsowania może być całkiem inny - ważne, żeby klasa zawierała metody zdefiniowane w interfejsie. dzięki temu mamy gwarancję, że taka zmiana nie wpłynie na resztą aplikacji, a ewentualne błędy w implementacji interfejsu Url zostaną wykryte przy uruchomieniu aplikacji.

Więcej informacji

Manual PHP.net