Wzór modułu
W inżynierii oprogramowania wzorzec modułu jest wzorcem projektowym służącym do wdrażania koncepcji modułów oprogramowania , zdefiniowanych przez programowanie modułowe , w języku programowania z niepełnym bezpośrednim wsparciem dla tej koncepcji.
Wzorzec ten można zaimplementować na kilka sposobów w zależności od języka programowania hosta, na przykład wzorzec projektowy singleton , zorientowane obiektowo elementy statyczne w klasie i globalne funkcje proceduralne. W Pythonie wzorzec jest wbudowany w język, a każdy plik .py jest automatycznie modułem. To samo dotyczy Ady, gdzie pakiet można uznać za moduł (podobnie jak klasa statyczna).
Definicja i struktura
Wzorzec projektowy oprogramowania modułowego zapewnia funkcje i strukturę składniową zdefiniowaną przez paradygmat programowania modułowego dla języków programowania, które mają niepełne wsparcie dla tej koncepcji.
Pojęcie
Podczas tworzenia oprogramowania kod źródłowy można podzielić na komponenty realizujące określoną funkcję lub zawierające wszystko, co jest niezbędne do wykonania określonego zadania. Jednym z takich podejść jest programowanie modułowe .
Koncepcja „modułu” nie jest w pełni obsługiwana w wielu popularnych językach programowania.
Cechy
Aby uznać, że Singleton lub dowolna grupa powiązanego kodu implementuje ten wzorzec, należy dostarczyć następujące cechy:
- Część kodu musi mieć dostęp globalny lub publiczny i być zaprojektowana do użytku jako kod globalny/publiczny. Dodatkowy kod prywatny lub chroniony może być wykonany przez główny kod publiczny.
- Moduł musi mieć funkcję inicjującą, która jest równoważna lub komplementarna z metodą konstruktora obiektów . Ta funkcja nie jest obsługiwana przez zwykłe przestrzenie nazw .
- Moduł musi mieć funkcję finalizatora, która jest równoważna lub komplementarna z metodą destruktora obiektów. Ta funkcja nie jest obsługiwana przez zwykłe przestrzenie nazw.
- Członkowie wspierający mogą wymagać kodu inicjalizacji/finalizacji, który jest wykonywany przez funkcję inicjatora/finalizatora modułu.
- Większość członków to funkcje, które wykonują operacje na elementach zewnętrznych w stosunku do klasy, dostarczanych jako argumenty przez wywołanie funkcji. Takie funkcje to „narzędzia”, „narzędzia” lub „biblioteki”.
Implementacje
Semantyka i składnia każdego języka programowania mogą wpływać na implementację tego wzorca.
Obiektowe języki programowania
Jawa
Chociaż Java obsługuje pojęcie przestrzeni nazw , czyli zredukowanej wersji modułu, w niektórych scenariuszach korzystne jest zastosowanie wzorca projektowego zamiast używania przestrzeni nazw.
W poniższym przykładzie zastosowano wzorzec singleton.
Definicja
konsole pakietowe ; importuj java.io.InputStream ; importuj java.io.PrintStream ; public final class MainModule { private static MainModule singleton = null ; public InputStream input = null ; publiczne wyjście PrintStream = null ; public PrintStream błąd = null ;
private MainModule () { // nic nie robi celowo !!! } // ... public static MainModule getSingleton () { if ( MainModule . singleton == null ) { MainModule . singleton = nowy moduł główny (); } zwraca moduł główny . singleton ; } // ... public void przygotuj
() { //System.out.println("konsole::przygotowanie();"); to . wejście = nowy strumień wejściowy (); to . wyjście = nowy Strumień Druku (); to . błąd = nowy strumień wydruku (); } public void unprepare () { this . wyjście = zero ; to . wejście = zero
; to . błąd = zero ; //System.out.println("konsole::unprepare();"); } // ... public void printNewLine () { System . na zewnątrz println (); } public void printString ( wartość ciągu ) { System . na zewnątrz drukuj ( wartość ); } pustka publiczna
printInteger ( wartość int ) { System . na zewnątrz drukuj ( wartość ); } public void printBoolean ( wartość logiczna ) { System . na zewnątrz drukuj ( wartość ); } public void scanNewLine () { // do zrobienia: ... } public void scanString (
Wartość ciągu ) { // zadanie: ... } public void scanInteger ( wartość int ) { // zadanie: ... } public void scanBoolean ( wartość logiczna ) { // zadanie: ... } // ... }
Realizacja
importuj konsole.* ; class ConsoleDemo { public static MainModule console = null ; public static void przygotować () { console = MainModule . pobierzSingleton (); konsola . przygotować (); } public static void unprepare () { console . nieprzygotowany (); }
public static void wykonaj ( String [] args ) { console . printString ( "Witaj świecie" ); konsola . drukujNowaLinia (); konsola . skanujNowąLinię (); } public static void main ( String [] args ) { przygotować (); wykonaj ( argumenty );
nieprzygotowany (); } }
C# (C Sharp .NET)
C# , podobnie jak Java, obsługuje przestrzenie nazw, chociaż wzorzec pozostaje przydatny w określonych przypadkach.
W poniższym przykładzie zastosowano wzorzec singleton.
Definicja
przy użyciu Systemu ; przy użyciu System.IO ; używając System.Text ; konsole przestrzeni nazw ; publiczna zapieczętowana klasa MainModule { private static MainModule Singleton = null ; public InputStream wejście = null ; public OutputStream wyjście = null ; publiczny błąd strumienia błędów =
zero ; // ... public MainModule () { // nic nie robi celowo !!! } // ... public static MainModule GetSingleton () { if ( MainModule . Singleton == null ) { MainModule . Singleton = nowy moduł główny (); } zwraca moduł główny . Singleton ; } // ...
public void Przygotuj () { //System.WriteLine("console::prepare();"); to . wejście = nowy strumień wejściowy (); to . wyjście = nowy strumień wyjściowy (); to . błąd = nowy strumień błędów (); } public void Unprepare () { this . wyjście = null ; to .
wejście = zero ; to . błąd = zero ; //System.WriteLine("console::unprepare();"); } // ... public void PrintNewLine () { System . Konsola . Wiersz zapisu ( "" ); } public void PrintString ( wartość ciągu ) { System . Konsola . Pisać
( Wartość ); } public void PrintInteger ( wartość całkowita ) { System . Konsola . Napisz ( wartość ); } public void PrintBoolean ( wartość logiczna ) { System . Konsola . Napisz ( wartość ); } public void ScanNewLine () {
// do zrobienia: ... } public void ScanString ( String Value ) { // do zrobienia: ... } public void ScanInteger ( Integer Value ) { // do zrobienia: ... } public void ScanBoolean ( Wartość logiczna ) { // do zrobienia: ... } // ... }
Realizacja
class ConsoleDemo { publiczne statyczne konsole . Konsola modułu głównego = null ; public static void Przygotuj () { Console = Consoles . Moduł główny . GetSingleton (); Konsola . Przygotuj (); } public static void Unprepare () { Console . nieprzygotowany ();
} public static void Wykonaj () { Konsola . PrintString ( "Witaj świecie" ); Konsola . DrukujNowąLinię (); Konsola . SkanujNowąLinię (); } public static void Main () { Przygotuj (); Wykonaj ( argumenty ); nieprzygotowany (); } }
Języki programowania oparte na prototypach
JavaScript
JavaScript jest powszechnie używany do automatyzacji stron internetowych.
Definicja
funkcja ConsoleClass () { var Wejście = null ; var Dane wyjściowe = null ; var Błąd = null ; // ... to . przygotuj = funkcja () { to . Wejście = nowy strumień wejściowy (); to . Wyjście = nowy Strumień Wyjścia (); to . Błąd
= nowy strumień błędów (); } to . nieprzygotowany = funkcja () { to . wejście = null ; to . Wyjście = null ; to . Błąd = null ; } // ... var printNewLine = function () { // kod, który drukuje nową linię } var printString =
function ( params ) { // kod, który drukuje parametry } var printInteger = function ( params ) { // kod, który drukuje parametry } var printBoolean = function ( params ) { // kod, który drukuje parametry } var ScanNewLine = function () { / / kod, który szuka nowej linii } var ScanString
= function ( params ) { // kod wprowadzający dane do parametrów } var ScanInteger = function ( params ) { // kod wprowadzający dane do parametrów } var ScanBoolean = function ( params ) { // kod wprowadzający dane do parametrów } / / ... }
Realizacja
funkcja ConsoleDemo () { var Konsola = null ; var przygotować = function () { Console = new ConsoleClass (); Konsola . przygotować (); } var unprepare = function () { Konsola . nieprzygotowany (); } var uruchom = funkcja () { Konsola
. printString ( "Witaj świecie" ); Konsola . drukujNowaLinia (); } var main = funkcja () { this . przygotować (); to . biegnij (); to . nieprzygotowany (); } }
Proceduralne języki programowania
Ten wzorzec może być postrzegany jako proceduralne rozszerzenie języków zorientowanych obiektowo.
Chociaż paradygmaty programowania proceduralnego i modułowego są często używane razem, istnieją przypadki, w których język programowania proceduralnego może nie w pełni obsługiwać moduły, co wymaga implementacji wzorca projektowego.
PHP (proceduralny)
Ten przykład dotyczy proceduralnego PHP przed przestrzeniami nazw (wprowadzony w wersji 5.3.0). Zaleca się, aby każdemu członkowi modułu nadano przedrostek związany z nazwą pliku lub modułu, aby uniknąć kolizji identyfikatorów.
Definicja
<?php // nazwa pliku: console.php function console_prepare () { // kod, który przygotowuje "konsolę" } function console_unprepare () { // kod, który nie przygotowuje "konsoli" } // ... function console_printNewLine () { // kod, który wyświetla nowy wiersz } function console_printString ( /* String */ Value ) { // kod, który drukuje parametry }
function console_printInteger ( /* Integer */ Value ) { // kod, który drukuje parametry } function console_printBoolean ( /* Boolean */ Value ) { // kod, który wyświetla parametry } function console_scanNewLine () { // kod, który szuka nowej linii } function console_scanString ( /* String */ Value ) { // kod przechowujący dane w parametrach
} function console_scanInteger ( /* liczba całkowita */ wartość ) { // kod przechowujący dane w parametrach } function console_scanBoolean ( /* wartość logiczna */ wartość ) { // kod przechowujący dane w parametrach }
Realizacja
// nazwa pliku: consoledemo.php require_once ( "console.php" ); funkcja consoledemo_prepare () { console_prepare (); } funkcja consoledemo_unprepare () { console_unprepare (); } funkcja consoledemo_execute () { console_printString ( "Witaj świecie" ); console_printNewLine (); console_scanNewLine (); } funkcja
consoledemo_main () { consoledemo_prepare (); Consoledemo_execute (); consoledemo_unprepare (); }
C
Zauważ, że ten przykład dotyczy proceduralnego C bez przestrzeni nazw. Zaleca się, aby każdemu elementowi modułu nadano przedrostek związany z nazwą pliku lub modułu, aby uniknąć kolizji identyfikatorów.
Moduł nagłówka definicji
// nazwa pliku: "consoles.h" #include <stdio.h> #include <string.h> #include <ctype.h> void consoles_prepare (); void consoles_unprepare (); // ... void consoles_printNewLine (); void consoles_printString ( znak * Wartość ); void consoles_printInteger ( wartość int ); void consoles_printBoolean ( bool
wartość ); void consoles_scanNewLine (); void consoles_scanString ( znak * Wartość ); void consoles_scanInteger ( int * Wartość ); void consoles_scanBoolean ( bool * Wartość );
Definicja modułu ciała
// nazwa pliku: "consoles.c" #include <stdio.h> #include <string.h> #include <ctype.h> #include <consoles.h> void consoles_prepare () { // kod przygotowujący konsolę } void consoles_unprepare () { // kod, który cofa przygotowanie konsoli } // ... void consoles_printNewLine () { printf ( " \n " ); }
void consoles_printString ( char * Wartość ) { printf ( "%s" , Wartość ); } void consoles_printInteger ( int Wartość ) { printf ( "%d" & Wartość ) ; } void consoles_printBoolean ( wartość bool ) { printf (( wartość ) ?
( "prawda" ) : ( "fałsz" )); } void consoles_scanNewLine () { getch (); } void consoles_scanString ( char * Wartość ) { scanf ( "%s" , Wartość ); } void consoles_scanInteger ( int * Wartość ) { scanf ( "%d"
0
, wartość ); } void consoles_scanBoolean ( bool * Wartość ) { char temp [ 512 ]; scanf ( "%s" , temp ); * Wartość = ( strcmp ( Temp , "true" ) == ); }
Realizacja
// nazwa pliku: "consoledemo.c" #include <stdio.h> #include <string.h> #include <ctype.h> #include <consoles.h> void consoledemo_prepare () { consoles_prepare (); } nieważne consoledemo_unprepare () { consoles_unprepare (); } int consoledemo_execute () { consoles_printString ( "Witaj świecie" );
0
0
consoles_printNewLine (); consoles_scanNewLine (); powrót ; } int main () { Kod błędu Wynik = ; konsola demo_przygotowanie (); Kod błędu = consoledemo_execute (); consoledemo_unprepare (); zwróć kod błędu ; }
Pascal proceduralny
Zauważ, że ten przykład dotyczy proceduralnego, niemodułowego Pascala. Wiele dialektów Pascala obsługuje przestrzeń nazw, zwaną „jednostkami”. Niektóre dialekty obsługują również inicjalizację i finalizację.
Jeśli przestrzenie nazw nie są obsługiwane, zaleca się nadanie wszystkim nazwom członków przedrostka związanego z nazwą pliku lub nazwą modułu, aby zapobiec kolizjom identyfikatorów.
Definicja
konsole jednostek ; (* nazwa pliku: "consoles.pas" *) używa crt ; przygotowanie procedury () ; begin (* kod przygotowujący konsolę *) end ; procedura nieprzygotowana () ; begin (* kod, który nie przygotowuje konsoli *) end ; // ... procedura printNewLine () ; rozpocznij WriteLn () ; koniec ; procedura printString (
Wartość : łańcuch ) ; zacznij pisać ( wartość ) ; koniec ; procedura printInteger ( Wartość : liczba całkowita ) ; zacznij pisać ( wartość ) ; koniec ; procedura printBoolean ( Wartość : boolean ) ; rozpocznij if ( wartość ), a następnie rozpocznij pisanie
( „prawda” ) ; end else begin Write ( 'false' ) ; koniec ; koniec ; procedura scanNewLine () ; rozpocznij SeekEoLn () ; koniec ; procedura scanString ( Wartość : string ) ; rozpocząć ReadLn ( Wartość ) ; koniec ; procedura
scanInteger ( wartość : liczba całkowita ) ; rozpocząć ReadLn ( Wartość ) ; koniec ; procedura scanBoolean ( Wartość : Boolean ) ; zmienna temperatura : ciąg znaków ; rozpocząć OdczytLn ( temp ) ; if ( Temp = 'true' ), a następnie rozpocznij Wartość :=
prawda ; koniec else begin Wartość := false ; koniec ; koniec ;
Realizacja
program demo konsoli ; // nazwa pliku: "consoles.pas" używa consoles ; przygotowanie procedury () ; uruchom konsole . przygotować () ; koniec ; procedura nieprzygotowana () ; uruchom konsole . nieprzygotowany () ; koniec ; wykonanie funkcji () : Liczba całkowita ; uruchom konsole .
0
printString ( 'Witaj świecie' ) ; konsole . printNewLine () ; konsole . scanNewLine () ; wykonaj := ; koniec ; zacznij przygotowywać () ; wykonaj () ; nieprzygotowany () ; koniec .
Porównania z innymi koncepcjami
Przestrzenie nazw
Zarówno przestrzenie nazw , jak i moduły pozwalają na grupowanie kilku powiązanych ze sobą podmiotów według jednego identyfikatora, aw niektórych sytuacjach używane zamiennie. Dostęp do tych jednostek można uzyskać globalnie. Główny cel obu koncepcji jest taki sam.
W niektórych scenariuszach przestrzeń nazw wymaga, aby elementy globalne, które ją tworzą, zostały zainicjowane i sfinalizowane przez wywołanie funkcji lub metody.
W wielu językach programowania przestrzenie nazw nie są bezpośrednio przeznaczone do obsługi procesu inicjalizacji ani procesu finalizacji, a zatem nie są odpowiednikami modułów. To ograniczenie można obejść na dwa sposoby. W przestrzeniach nazw , które obsługują funkcje globalne, funkcja inicjalizacji i funkcja finalizacji są kodowane bezpośrednio i wywoływane bezpośrednio w głównym kodzie programu.
Klasy i przestrzenie nazw
Klasy są czasami używane jako lub z przestrzeniami nazw . W językach programowania, które nie obsługują przestrzeni nazw (np. JavaScript), ale obsługują klasy i obiekty, klasy często zastępują przestrzenie nazw. Te klasy zwykle nie są tworzone i składają się wyłącznie z elementów statycznych.
Klasy singleton i przestrzenie nazw
W zorientowanych obiektowo językach programowania, w których przestrzenie nazw nie są w pełni obsługiwane, wzorzec singleton może być używany zamiast statycznych elementów w klasie, której nie można utworzyć.
Związek z innymi wzorcami projektowymi
Wzorzec modułu można zaimplementować przy użyciu specjalizacji wzorca singleton. Jednakże w tej samej klasie można stosować i łączyć inne wzorce projektowe.
Ten wzór może być używany jako dekorator , odważnik lub adapter .
Moduł jako wzorzec projektowy
Wzór modułu można uznać za wzór kreacyjny i wzór strukturalny . Zarządza tworzeniem i organizacją innych elementów oraz grupuje je tak, jak robi to wzorzec strukturalny.
Obiekt, który stosuje ten wzorzec, może zapewnić odpowiednik przestrzeni nazw , zapewniając proces inicjalizacji i finalizacji klasy statycznej lub klasy ze statycznymi elementami z czystszą, bardziej zwięzłą składnią i semantyką .
Obsługuje określone przypadki, w których klasę lub obiekt można uznać za ustrukturyzowane dane proceduralne. I odwrotnie, migruj ustrukturyzowane, proceduralne dane i uważaj je za zorientowane obiektowo.
Zobacz też
- Wzór projektowy
- Wzorce projektowe (E. Gamma i in.)
- Wzór singletona
- Wzór adaptera