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.

The object module pattern expressed in UML.
Wzorzec modułu obiektowego wyrażony w języku UML .

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ż