Funkce, metody a procedury – jaký je mezi nimi rozdíl

13
Bře/11
3

Všechny tři (funkce, metody i procedury) jsou si poměrně dost podobné – je to pojmenovaná posloupnost příkazů, část programu, kterou můžeme opakovaně volat z jiných částí programu.

Formální pohled

Z formálního hlediska se řídíme názvosloví daného programovacího jazyka.

  • Funkce je pojem z funkcionálního programování, vyskytuje se v jazycích jako je JavaScript nebo třeba Haskell.
  • Jako metody označujeme funkce v objektově orientovaném programování (OOP), kde dochází k propojení datových struktur a funkčního kódu do objektů. Metody nalezneme v jazycích jako Java, Smalltalk a dalších.
  • Procedura je pojem známý z procedurálních jazyků. Nalezneme je např. v Pascalu, ale také v některých databázových systémech – tzv. uložené procedury.

Na funkce, metody a procedury se ale můžeme dívat z věcného hlediska (podle jejich vlastností a smyslu) a odchýlit se od terminologie konkrétního programovacího jazyka.

Funkce

Funkce v programování má velmi blízko k funkcím, jak je chápeme v matematice. Funkce má určitý definiční obor (typ vstupních parametrů) a určitý obor hodnot (typ návratové hodnoty).

Příklad jednoduché funkce v JavaScriptu:

function sečti(a, b) {
	return a + b;
}

V objektových jazycích jako Java máme sice metody a ne funkce, to nám ale nebrání v nich funkce psát:

public static int sečti(int a, int b) {
	return a + b;
}

Přestože sečti() je formálně metoda (veřejná a statická), ve skutečnosti se jedná o funkci a třída zde plní pouze úlohu jmenného prostoru (společně s názvem balíčku) a nevytváříme její instance (resp. nemusíme). Jedná se o návrhový vzor Knihovní třída.

V Javě najdeme takové funkce např. ve třídě java.lang.Math. Následující funkce vrací větší ze dvou čísel:

int x = java.lang.Math.max(a, b);

Procedura

Procedura je zvláštním případem funkce – nemá návratovou hodnotu a nemusí mít ani vstupní parametry. Používají se často při dávkovém zpracování – např. každou hodinu zavoláme proceduru, která zpracuje objednávky, které se nashromáždily v databázi, a předá je do jiného systému.

Příklad velmi jednoduché procedury v PostgreSQL, která sečte u dosud nesečtených řádků hodnoty sloupců a a b a výsledek uloží do sloupce c:

CREATE OR REPLACE FUNCTION sčítej()
RETURNS void AS
$BODY$
	UPDATE tabulka_součtů
	SET c = a + b
	WHERE c IS NULL
$BODY$
LANGUAGE sql VOLATILE;

Tato procedura je napsaná v jazyce SQL – kromě toho můžeme psát plpgsql, který nám umožní tvořit i velmi složité procedury, případně můžeme použít jazyk C či jiný.

Výše uvedená procedura (formálně funkce) nemá žádné vstupní parametry ani návratovou hodnotu, je to prostě jen posloupnost příkazů jazyka, kterou jsme si nějak pojmenovali a uložili.

Zajímavostí je, že procedury můžou mít parametry – a to nejen klasické vstupní, ale i výstupní (nebo vstupně/výstupní). Procedura s výstupním parametrem nám slouží podobně jako funkce – přestože nemá klasickou návratovou hodnotu. Proceduře můžeme např. předat nějaká data a ona nám je upraví.

Takové procedury si můžeme simulovat i v jazyce Java. Mějme nějakou přepravku (strukturu):

public class Osoba {
	public String jméno;
	public String příjmení;
	public String celéJméno;
}

A proceduru:

public class UloženéProcedury {
	public void sestavCeléJméno(Osoba o) {
		o.celéJméno = o.jméno + " " + o.příjmení;
	}
}

Proceduře předáme data (osobu) a dojde k jejich požadované úpravě. Ovšem pokud programujete tímto stylem v Javě, pravděpodobně děláte něco špatně a připravujete se o hlavní výhody OOP.

Poznámka: pozor ale na předávání hodnotou – pokud uvnitř procedury v Javě přiřadíte do proměnné o novou osobu, tato změna se mimo danou proceduru nijak neprojeví – původní osoba zůstane netknuta. Můžeme ale pracovat s atributy původní osoby – v takovém případě kód procedury i kód který ji zavolal „vidí“ stejnou instanci osoby.

Metoda

Metody jsou součástí třídy a (pokud nejsou statické) jsou úzce spjaté s danou instancí třídy (objektem). Nejsou to tedy funkce pracující s nějakými globálními proměnnými a daty, ale mají přístup k proměnným dané instance (v tomto případě osoby).

Předchozí příklad můžeme přepsat „objektovějším“ způsobem:

public class Osoba {

	public String jméno;
	public String příjmení;

	public String getCeléJméno() {
		return jméno + " " + příjmení;
	}
}

Třídu s uloženými procedurami vůbec nepotřebujeme a požadovanou funkcionalitu přesuneme blíže k datům (do třídy Osoba). Všimněte si, že ani nemusíme ukládat vypočtenou hodnotu celéJméno – vypočteme ji až ve chvíli, kdy ji někdo bude potřebovat, tzn. zavolá metodu getCeléJméno().

Dále změníme přístupnost proměnných z public na private a vytvoříme přístupové metody. Zbytek programu totiž nemá zajímat interní implementace dané třídy (jak jsou data uložena), ale jen její rozhraní, API.

public class Osoba {

	private String jméno;
	private String příjmení;

	public String getCeléJméno() {
		return jméno + " " + příjmení;
	}

	public String getJméno() {
		return jméno;
	}

	public String getPříjmení() {
		return příjmení;
	}

	public void setJméno(String jméno) {
		this.jméno = jméno;
	}

	public void setPříjmení(String příjmení) {
		this.příjmení = příjmení;
	}
}

Tomu se říká zapouzdření. Díky tomu můžeme změnit interní způsob uložení dat (např. neukládat jméno jako řetězec ale jako pole bajtů v určitém kódování) nebo přidat nějaké dodatečné operace prováděné při čtení nebo zápisu hodnot (do metod get…() a set…()

Něčeho podobného, přestože se to bude jmenovat funkce, můžeme dosáhnout v JavaScriptu – příklad z Wikipedie:

// Definice konstruktoru 
function auto(znacka, spz) { 
	this.znacka = znacka; 
	this.spz = spz; 
	this.vypisZnacku = function(){alert(this.znacka); };
}
//vytvoření nového auta
var moje_auto = new auto("mercedes", "3A4983");

V PostgreSQL metody nenajdeme, ale přesto můžeme dosáhnout určitého propojení dat a operací – byť to nebude jako v OOP. Můžeme využít tzv. spouští (triggerů), které se provedou při operacích nad tabulkou.

Pomocí následujícího triggeru a příslušné funkce:

CREATE OR REPLACE FUNCTION tabulka_součtů_sčítej() RETURNS TRIGGER AS '
BEGIN
	new.c := new.a + new.b;
	return new;
END;
' LANGUAGE 'plpgsql';

CREATE TRIGGER tabulka_součtů_trigger
  BEFORE INSERT ON tabulka_součtů
  FOR EACH ROW EXECUTE PROCEDURE tabulka_součtů_sčítej();

dosáhneme podobného chování jako kdybychom v Javě napsali:

public class Součty {

	int a;
	int b;
	int c;

	public Součty(int a, int b) {
		this.a = a;
		this.b = b;
		this.c = a + b;
	}
}

Spouště v databázi můžeme navázat na operace INSERT, UPDATE, DELETE. Ačkoli tabulky nejsou třídy a procedury/funkce nejsou metody, můžeme se částečně přiblížit přístupu používanému v OOP.

Závěr

Přestože každý programovací jazyk má určité paradigma (některé jich mají více), záleží hlavně na programátorovi, jakým stylem bude pracovat. Aneb jak se říká: opravdový programátor umí psát FORTRANské programy v kterémkoliv jazyce :-)

Odkazy a zdroje

Void funkce jeste neni procedura. Asi nejmarkantnejsi rozdil mezi funkci a procedurou (ze ktereho pak vyplivaji dalsi konsekvence) je ten, ze se funkce vola z prikazu SELECT, kdezto procedura se vola prikazem CALL. V DB2, Oracle, MSSQL maji funkce dost omezenou funkcionalitu (vuci proceduram). V PostgreSQL jsou tato omezeni mensi, jelikoz PostgreSQL nema podporu procedur, ale stale tam jsou - napr. explicitni rizeni vnejsi transakce neni mozne resit uvnitr funkce.

procedura vs. funkce

xkucf03
11:11 odpoledne on Březen 17th, 2011

jj, ale to je spíš ten formální pohled – věcně to beru jako proceduru, i když ji budu volat přes SELECT a v terminologii daného systému se to jmenuje funkce. Skutečná funkce je jako filtr na data (vstup→výstup), kdežto procedura jen jako uložená a pojmenovaná posloupnost příkazů.

ona to není formalita - uvnitř SELECTu nelze přerušit posloupnost stransakcí, pokud se blok kódu volá SELECTem, tak musí být dopředu jasné, jak bude vypadat výstup - není možný dynamický resultset nebo nedej Bože multirecordset.