Ugrás a tartalomhoz

Parancs programtervezési minta

A Wikipédiából, a szabad enciklopédiából
A lap korábbi változatát látod, amilyen TurkászBot (vitalap | szerkesztései) 2020. október 4., 11:36-kor történt szerkesztése után volt. Ez a változat jelentősen eltérhet az aktuális változattól. (Kategória mozgatása (WP:BÜ), apróbb javítások)

Az objektumorientált programozásban, a parancs minta egy viselkedési forma, amelyben egy objektumot használunk arra, hogy reprezentáljunk és párosítsunk minden olyan információt, amely szükséges lehet egy metódus későbbi meghívásához. Ezen információk közé tartozik a metódus neve, az objektum amely birtokolja a metódust és az értékeit a metódus paramétereinek. Az alábbi négy pont mindig együtt említendő a parancs mintával kapcsolatban, melyek nem mások mint a

  • (Command) parancs,
  • (Receiver) vevő,
  • (Invoker) felhasználó [meghívó],
  • (Client) ügyfél.

A parancs objektumnak van egy vevő objektuma és ennek meghívja egy metódusát. A vevő ezután elvégzi a munkát. A parancs objektum el van különítve egy felhasználó objektumnak, amely meghívja a parancsot. Minden parancs objektum átadható ugyanannak a felhasználó objektumnak. Mind a felhasználó objektum és néhány parancs objektum az ügyfél objektum birtokában van. Az ügyfél tartalmazza a döntéshozatalt, hogy melyik parancsot kell végrehajtani, az adott ponton. Egy parancs végrehajtása azt jelenti, hogy egy parancs objektumot átadunk a felhasználó objektumnak. Lásd a példákat lentebb.

A Parancs objektumok használata könnyebbé teszi az általános komponensek létrehozását, amik szükségesek a „Delegate”, szekvenciális, vagy végrehajtó metódusok meghívásához anélkül, hogy tudnánk a "class"-unk metódusát, vagy annak paramétereit. A felhasználó (Invoker) objektum használata lehetőséget biztosít a naplózásra a parancs végrehajtások könnyű elvégezhetőségének érdekében, amennyiben végrehajtunk különböző parancsokat, amelyek egy felhasználó objektum által vannak vezérelve, anélkül, hogy az ügyfél tudatában lenne naplózásnak, vagy módoknak.

Felhasználása

A parancs objektumok az implementáláshoz hasznosak.

GUI gombok és menü elemek

Swing és Borland Delphi programozási nyelvekben, az „Action” (művelet) egy parancs objektum. Amellett, hogy képes végrehajtani a kívánt parancsot, egy „Action” lehet egy társított ikon, egy billentyűzet gyorsbillentyű, tooltip, és így tovább. Egy eszköztár gomb, vagy egy menüpont összetevője létrejöhet kizárólag egy „Action” vezérlése végett.

Makró felvétel

Ha minden felhasználói „Action” (művelet) egy parancs objektum által van reprezentálva, akkor a program képes rögzíteni egy művelet szekvenciáját egyszerűen azáltal, hogy egy listát tart a parancs objektumokról, ahogy őket meghívták. Ezt követően „újrajátssza” ugyanazt a műveletet azáltal, hogy meghívja a parancs objektumon ugyanabban a szekvenciában. Ha a program "Script" motort is tartalmaz, minden parancs objektum implementálhat egy toScript() metódust, és a felhasználói műveletek akkor könnyebben rögzítve lehetnek "Script"-ként.

Mobil kódolás

Olyan nyelveket használva, mint például Java, ahol a kód "Stream"-elhető egy bizonyos távoli helyről egy másikra "URLClassloader"-ek és "Codebase"-k segítségével, ott a parancsok lehetővé tesznek egy újfajta viselkedéseket, a távoli helyeken. (EJB Command, Master Worker)

A többszintű visszavonás

Ha minden felhasználói művelet egy parancs objektum által van implementálva, akkor a program egy veremben tárolja a legutóbb használt végrehajtott parancsokat. Amikor a felhasználó vissza akar vonni egy parancsot, a program egyszerűen visszaadja a legutóbbi parancs objektumot, és végrehajtja az undo() metódust.

Networking

Lehetőség van rengeteg parancs objektum küldésére a hálózaton keresztül, hogy végrehajtsuk azt egy másik számítógépen.

Párhuzamos feldolgozás

Itt a parancsok „task”-ként, feladatokként vannak megírva egy megosztott erőforrás részére és több szál által vannak végrehajtva párhuzamosan. (Valószínűleg egy távoli számítógépen)

Folyamatjelzők

Feltételezzük, hogy egy program parancsok soraiból épül fel melyek sorban hajtódnak végre. Parancs objektumonként van egy getEstimatedDuration() metódusunk, mellyel, a program könnyen megbecsüli annak teljes hosszát (időtartamát). Ez tükröződhet egy folyamatjelző által, mely észszerűen mutatja, hogy milyen közel van a program ahhoz, hogy végezzen az összes feladattal.

Thread pools (Szálcsoportok)

Egy tipikus, általános célú szálcsoport osztálynak rendelkeznie kell egy publikus addTask() metódussal, amely hozzáad egy munkaeszközt egy belső feladat sorához (queue-hoz) várván a feladat befejezésére. Ez azt jelenti, hogy a szálcsoport meghívja a parancsokat a (queue-ból) sorból. A sor elemei parancs objektumok. Általában ezek az objektumok egy közös interfészt (common interface-t) implementálnak, mint például „java.lang.Runnable” amely lehetőséget nyújt a szálcsoportoknak, hogy végrehajtsák a parancsokat még akkor is, ha maga a szálcsoport osztály úgy van megírva, hogy nincs ismerete a konkrét feladatokról.

Tranzakciós viselkedés

Hasonlóan a visszavonáshoz, egy adatbázis motor, vagy egy szoftver telepítő tartalmazhat egy listát a műveletekről, amely végre lettek, vagy végre lesznek hajtva. Amennyiben valamelyikük nem sikerül, az összes többi visszafordítható vagy eldobható (általában „rollback”-nek hívják). Például, ha van két adatbázis tábla, melyek hivatkoznak egymásra, amiket frissíteni kell, és a második frissítés nem sikerül, a tranzakció visszaállítható, így az első tábla nem tartalmaz majd hibás hivatkozásokat.

Wizards (Varázslók)

Gyakran egy varázsló több oldalnyi konfigurációs lapot mutat be egyetlen művelet érdekében, mely csak akkor következik be, ha a felhasználó rákattint a „Kész” gombra az utolsó oldalon. Ezekben az esetekben a természetes út a felhasználói interfész kódjának és az alkalmazás kódjának szétválasztása érdekében, hogy implementáljuk a varázslót parancs objektum használatával. A parancs objektum akkor jön létre, amikor a varázsló először megjelenik. A varázsló minden oldalában tárolja a GUI változásokat a parancs objektumban, tehát az objektum annyira előrehaladott (belakott), amennyire a felhasználó halad. A „Kész” gomb egyszerűen elindít egy „execute()” hívást. Ezen a ponton kezdődik a parancs osztály munkája, a parancsok sorozatának végrehajtása.

Terminológia

A terminológiát arra használjuk, hogy leírjunk, a parancs tervezési minta implementációk nem következetesek és ennél fogva zavaró lehet. Ez a kétértelműség eredménye, a szinonimák és implementációk elfedhetik az eredeti mintát.

1. Kétértelműség

  • A „command” (parancs) kifejezés kétértelmű. Például a „move up” parancs. A „move up” utalhat egy egyszeri „single” parancsra, amelyet kétszer is végrehajthatunk, vagy utalhat két parancsra, melyek mindegyike ugyan azt tudja. Ha a korábbi parancs kétszer van hozzáadva a „vissza verembe”, akkor mindkét elem a veremben ugyan arra a parancs példányra utal/mutat. Ez megfelelő lehet abban az esetben, ha a parancsot vissza lehet vonni mindig ugyan abba az irányba. Mind a „Gang of Four” és a „Java” példa lejjebb használ interpretációt a parancs kifejezésben. Másfelől, ha az utóbbi parancsok hozzá vannak adva a „vissza verembe”, akkor a verem két különálló objektumra mutat. Ez megfelelő lehet abban az esetben, ha a veremben, példányonként tartalmazzák azon információkat, melyek engedélyezik a parancs visszavonását.
  • A(z) „Execute” (végrehajt) kifejezés is kétértelmű. Ez arra utal, hogy futtasd a kódot a parancs objektumok végrehajtási metódusai által. Habár a Windows Presentation Foundationben (WPF), egy parancs végrehajtottnak tekinthető, ha a parancsok végrehajtói metódusára hivatkozva van, de ez nem feltétlenül azt jelenti, hogy az alkalmazás kódja fut.

2. Szinonimák és homonimák

  • Kliens, forrás, felhasználó: a gomb, eszköztár gomb, vagy a menü elemek kattintása, a gyorsbillentyű gombok lenyomása a felhasználó által.
  • Parancs objektum, Irányított parancs objektum, Művelet objektum: Egy „singleton” objektum, mely birtokában van a gyorsbillentyű gomboknak, a képi gomboknak, a parancs szövegeknek, stb. kapcsolódnak a parancshoz. A parancs/művelet objektumok értesítik a megfelelő forrás/felhasználó objektumokat, amikor parancs/művelet objektumok elérhetősége megváltozik. Ez engedélyezi a gomboknak és a menü elemeknek, hogy inaktívvá váljanak (beszürküljenek) amikor egy parancs/művelet nem hajtható végre.
  • Vevő (fogadó), Cél objektum: az objektum amely a másolásért, beillesztésért, áthelyezésért felel. A vevő objektum birtokolja a metódust, amit parancsok végrehajtói metódusnak (command’s execute method) is neveznek. A vevő tipikusan a cél objektum is egyben. Például, ha egy vevő objektum egy kurzor és a metódus egy „moveUp” nevű metódus, akkor elvárható, hogy a kurzor a moveUp művelet célját képezi. Másfelől, ha a kód a parancs objektum által van definiálva, akkor a cél objektum teljesen más objektum lesz.
  • Parancs objektum, irányított esemény argumentumok, esemény objektumok: az objektum , amely el van választva a forrásból a parancs/műveleti objektumok, a cél objektumok, és a kód felé, amelyek végzik a munkát. Minden gomb kattintás, vagy gyorsbillentyű gomb eredménye egy új parancs/esemény objektum. Néhány implementáció több információt nyújt a parancs/esemény objektumnak. Egyéb implementációk parancs/esemény objektumokat tesznek egyéb esemény objektumba (Mint egy doboz, amiben egy még nagyobb doboz van)
  • Kezelő (Handler), ExecutedRoutedEventHandler, metódus, funkció (function): az aktuális kód amely már elvégzi a másolást, beillesztést, mozgatást, stb. Néhány implementációban a kezelő kód része a parancs/műveleti objektumnak. Más implementációkban a kód része a vevő/cél objektumoknak, és még további más implementációkban a kezelő kód külön van választva más egyéb objektumoktól.
  • Parancs menedzser, vissza menedzser, időzítő, sor (queue), diszpécser, felhasználó (invoker): egy objektum mely a parancs/esemény objektumot beleteszi egy „vissza verembe” (undo stack), vagy egy „helyrehoz verembe” (redo stack), vagy addig tartja a parancs/esemény objektumot, amíg egyéb objektumok késszé akarják tenni őket, vagy irányítják a parancs/esemény objektumot a megfelelő vevő/cél objektum felé vagy kezelő kódhoz.

3. Implementációk, melyek már túlmutatnak az eredeti parancs tervezési mintán

  • A Windows Presentation Foundation (WPF) bemutatta az irányított parancsokat, melyek kombinálják a parancs tervezési mintát az esemény feldolgozással. Ennek eredményeképpen a parancs objektum többé már nem tartalmaz hivatkozást a cél objektum felé, és az applikáció kódja felé sem. Ehelyett felhasználja a parancs objektumok meghívási parancsainak eredményeit egy úgynevezett „Executed Routed Event” – Végrehajtott irányított eseményt, amely a „Tunneling” vagy „Bubbling” események alatt fordulhat elő egy úgynevezett „Binding” objektumban, amely azonosítja a célt és az applikáció kódot, amely már el van végezve ezen a ponton.

Példák

Tekintsünk egy egyszerű kapcsolót, legyen a neve „Switch”. Ebben a példában konfigurálunk egy "Switch"-et két paranccsal: ez a kettő nem más mint, „kapcsoljuk LE” a lámpát, és „kapcsoljuk FEL” a lámpát.

Az előnye ennek a különös parancs tervezési minta implementációnak az az, hogy a "Switch" használható bármilyen eszköz által, nem csak a lámpa által. A "switch" a következő példában le és felkapcsolhatja a lámpát, de a "Switch"-ek konstruktora képes elfogadni bármilyen más "Subclass" parancsát aminek két paramétere van. Például úgy is be tudjuk állítani a "Swith"-et, hogy az ki vagy be kapcsoljon egy motort.

C#

A következő kód a „Parancs tervezési minta” egy implementációja C#-ban.

using System;
using System.Collections.Generic;
 
namespace CommandPattern
{
    public interface ICommand
    {
        void Execute();
    }
 
    /* The Invoker class */
    public class Switch
    {
        ICommand _closedCommand;
        ICommand _openedCommand;
 
        public Switch(ICommand closedCommand, ICommand openedCommand)
        {
            _closedCommand = closedCommand;
            _openedCommand = openedCommand;
 
        }
 
        //close the circuit/power on
        public void Close()
        {
            _closedCommand.Execute();
        }
 
        //open the circuit/power off
        public void Open()
        {
            _openedCommand.Execute();
        }
    }
 
    /* An interface that defines actions that the receiver can perform */
    public interface ISwitchable
    {
        void PowerOn();
        void PowerOff();
    }
 
    /* The Receiver class */
    public class Light : ISwitchable
    {
        public void PowerOn()
        {
            Console.WriteLine("The light is on");
        }
 
        public void PowerOff()
        {
            Console.WriteLine("The light is off");
        }
    }
 
    /* The Command for turning on the device - ConcreteCommand #1 */
    public class CloseSwitchCommand: ICommand
    {
        private ISwitchable _switchable;
 
        public CloseSwitchCommand(ISwitchable switchable)
        {
            _switchable = switchable;
        }
 
        public void Execute()
        {
            _switchable.PowerOn();
        }
    }
 
    /* The Command for turning off the device - ConcreteCommand #2 */
    public class OpenSwitchCommand : ICommand
    {
        private ISwitchable _switchable;
 
        public OpenSwitchCommand(ISwitchable switchable)
        {
            _switchable = switchable;
        }
 
        public void Execute()
        {
            _switchable.PowerOff();
        }
    }
 
    /* The test class or client */
    internal class Program
    {
        public static void Main(string[] args)
        {
            string arg = args.Length > 0 ? args[0].ToUpper() : null;
 
            ISwitchable lamp = new Light();
 
            //Pass reference to the lamp instance to each command
            ICommand switchClose = new CloseSwitchCommand(lamp);
            ICommand switchOpen = new OpenSwitchCommand(lamp);
 
 
            //Pass reference to instances of the Command objects to the switch
            Switch @switch = new Switch(switchClose, switchOpen);
 
 
            if (arg == "ON")
            {
                //Switch (the Invoker) will invoke Execute() (the Command) on the command object - _closedCommand.Execute();
                @switch.Close();
            }
            else if (arg == "OFF")
            {
                //Switch (the Invoker) will invoke the Execute() (the Command) on the command object - _openedCommand.Execute();
                @switch.Open();
            }
            else
            {
                Console.WriteLine("Argument \"ON\" or \"OFF\" is required.");
            }
        }
    }
}

Egy egyszerűbb példa:

/*IVSR: Command Pattern*/
using System;
using System.Collections;
using System.Linq;
 
namespace IVSR.Designpatterns.CommandPattern_demo
{
    #region ICommand Interface
    interface ICommand
    {
        string Name { get; set; }
        string Description { get; set; }
        void Run();
    }
    #endregion
 
    #region Command Invoker
    class CInvoker
    {
        // Array to hold list of commands
        private ArrayList listOfCommands = new ArrayList();
 
        //Default constructor to load commands
        public CInvoker()
        {
            LoadCommands();
        }
 
        //Loads the commands to arrylist
        private void LoadCommands()
        {
            listOfCommands.Add(new cmdOpen());
            listOfCommands.Add(new cmdClose());
            listOfCommands.Add(new cmdCreate());
            listOfCommands.Add(new cmdUpdate());
            listOfCommands.Add(new cmdRetrieve());
        }
 
        //Find command using foreach
        public ICommand GetCommand(string name)
        {
            foreach (var item in listOfCommands)
            {
                ICommand objCmd = (ICommand)item;
                if (objCmd.Name == name)
                {
                    return objCmd; //return the command found
                }
            }
            return null; //return if no commands are found
        }
    }
    #endregion
 
    #region Commands
 
    class cmdOpen : ICommand //command 1
    {
        private string _name = "open", _description = "opens a file";
        public string Name { get { return _name; } set { _name = value; } }
 
        public string Description { get { return _description; } set { _description = value; } }
 
        public void Run() { Console.WriteLine("running open command"); }
    }
 
    class cmdClose : ICommand //command 2
    {
        private string _name = "close", _description = "closes a file";
        public string Name { get { return _name; } set { _name = value; } }
        public string Description { get { return _description; } set { _description = value; } }
        public void Run() { Console.WriteLine("running close command"); }
    }
 
    class cmdCreate : ICommand //command 3
    {
        private string _name = "create", _description = "creates a file";
        public string Name { get { return _name; } set { _name = value; } }
        public string Description { get { return _description; } set { _description = value; } }
        public void Run() { Console.WriteLine("running create command"); }
    }
 
    class cmdUpdate : ICommand //Command 4
    {
        private string _name = "update", _description = "updates a file";
        public string Name { get { return _name; } set { _name = value; } }
        public string Description { get { return _description; } set { _description = value; } }
        public void Run() { Console.WriteLine("running update command"); }
    }
 
    class cmdRetrieve : ICommand //command 5
    {
        private string _name = "retrieve", _description = "retrieves a file";
        public string Name { get { return _name; } set { _name = value; } }
        public string Description { get { return _description; } set { _description = value; } }
        public void Run() { Console.WriteLine("running Retrieve command"); }
    }
    #endregion
 
    #region MAIN
    class Program
    {
        static void Main(string[] args)
        {
            //Command pattern example
            CInvoker cmdInvoker = new CInvoker();
            ICommand cmd1 = cmdInvoker.GetCommand("open");
            cmd1.Run();
            cmdInvoker.GetCommand("update").Run();
            //or
            new CInvoker().GetCommand("close").Run();
        }
    }
    #endregion
}

JAVA

import java.util.List;
import java.util.ArrayList;
 
/* The Command interface */
public interface Command {
   void execute();
}
 
/* The Invoker class */
public class Switch {
   private List<Command> history = new ArrayList<Command>();
 
   public void storeAndExecute(Command cmd) {
      this.history.add(cmd); // optional
      cmd.execute();
   }
}
 
/* The Receiver class */
public class Light {
 
   public void turnOn() {
      System.out.println("The light is on");
   }
 
   public void turnOff() {
      System.out.println("The light is off");
   }
}
 
/* The Command for turning on the light - ConcreteCommand #1 */
public class FlipUpCommand implements Command {
   private Light theLight;
 
   public FlipUpCommand(Light light) {
      this.theLight = light;
   }
 
   public void execute(){
      theLight.turnOn();
   }
}
 
/* The Command for turning off the light - ConcreteCommand #2 */
public class FlipDownCommand implements Command {
   private Light theLight;
 
   public FlipDownCommand(Light light) {
      this.theLight = light;
   }
 
   public void execute() {
      theLight.turnOff();
   }
}
 
/* The test class or client */
public class PressSwitch {
   public static void main(String[] args){
      Light lamp = new Light();
      Command switchUp = new FlipUpCommand(lamp);
      Command switchDown = new FlipDownCommand(lamp);
 
      Switch mySwitch = new Switch();
 
      switch(args[0]) {
         case "ON":
            mySwitch.storeAndExecute(switchUp);
         break;
         case "OFF":
            mySwitch.storeAndExecute(switchDown);
         break;
         default:
            System.out.println("Argument \"ON\" or \"OFF\" is required.");
       }
   }
}

Python

class Switch(object):
    """The INVOKER class"""
    @classmethod
    def execute(cls, command):
        command.execute()
 
class Command(object):
    """The COMMAND interface"""
    def __init__(self, obj):
        self._obj = obj
 
    def execute(self):
        raise NotImplemented
 
class TurnOnCommand(Command):
    """The COMMAND for turning on the light"""
    def execute(self):
        self._obj.turn_on()
 
class TurnOffCommand(Command):
    """The COMMAND for turning off the light"""
    def execute(self):
        self._obj.turn_off()
 
class Light(object):
    """The RECEIVER class"""
    def turn_on(self):
        print("The light is on")
 
    def turn_off(self):
        print("The light is off")
 
class LightSwitchClient(object):
    """The CLIENT class"""
    def __init__(self):
        self._lamp = Light()
        self._switch = Switch()
 
    def switch(self, cmd):
        cmd = cmd.strip().upper()
        if cmd == "ON":
            Switch.execute(TurnOnCommand(self._lamp))
        elif cmd == "OFF":
            Switch.execute(TurnOffCommand(self._lamp))
        else:
            print("Argument 'ON' or 'OFF' is required.")
 
# Execute if this file is run as a script and not imported as a module
if __name__ == "__main__":
    light_switch = LightSwitchClient()
    print("Switch ON test.")
    light_switch.switch("ON")
    print("Switch OFF test.")
    light_switch.switch("OFF")
    print("Invalid Command test.")
    light_switch.switch("****")

Scala

/* The Command interface */
trait Command {
   def execute()
}
 
/* The Invoker class */
class Switch {
   private var history: List[Command] = Nil
 
   def storeAndExecute(cmd: Command) {
      cmd.execute()
      this.history :+= cmd
   }
}
 
/* The Receiver class */
class Light {
   def turnOn() = println("The light is on")
   def turnOff() = println("The light is off")
}
 
/* The Command for turning on the light - ConcreteCommand #1 */
class FlipUpCommand(theLight: Light) extends Command {
   def execute() = theLight.turnOn()
}
 
/* The Command for turning off the light - ConcreteCommand #2 */
class FlipDownCommand(theLight: Light) extends Command {
   def execute() = theLight.turnOff()
}
 
/* The test class or client */
object PressSwitch {
   def main(args: Array[String]) {
      val lamp = new Light()
      val switchUp = new FlipUpCommand(lamp)
      val switchDown = new FlipDownCommand(lamp)
 
      val s = new Switch()
 
      try {
         args(0).toUpperCase match {
            case "ON" => s.storeAndExecute(switchUp)
            case "OFF" => s.storeAndExecute(switchDown)
            case _ => println("Argument \"ON\" or \"OFF\" is required.")
         }
      } catch {
         case e: Exception => println("Arguments required.")
      }
   }
}

JavaScript

/* The Invoker function */
var Switch = function(){
    var _commands = [];
    this.storeAndExecute = function(command){
        _commands.push(command);
        command.execute();
    }
}
 
/* The Receiver function */
var Light = function(){
    this.turnOn = function(){ console.log ('turn on') };
    this.turnOff = function(){ console.log ('turn off') };
}
 
/* The Command for turning on the light - ConcreteCommand #1 */
var FlipUpCommand = function(light){
    this.execute = function() { light.turnOn() };
}
 
/* The Command for turning off the light - ConcreteCommand #2 */
var FlipDownCommand = function(light){
    this.execute = function() { light.turnOff() };
}
 
var light = new Light();
var switchUp = new FlipUpCommand(light);
var switchDown = new FlipDownCommand(light);
var s = new Switch();
 
s.storeAndExecute(switchUp);
s.storeAndExecute(switchDown);

Coffescript:
# The Invoker function
class Switch
   _commands = []
   storeAndExecute: (command) ->
     _commands.push(command)
     command.execute()
 
#  The Receiver function
class Light
  turnOn: ->
    console.log ('turn on')
  turnOff: ->
    console.log ('turn off')
 
# The Command for turning on the light - ConcreteCommand #1
class FlipUpCommand
 constructor: (@light) ->
 
 execute: ->
   @light.turnOn()
 
# The Command for turning off the light - ConcreteCommand #2
class FlipDownCommand
 constructor: (@light) ->
 
 execute: ->
   @light.turnOff()
 
light = new Light()
switchUp = new FlipUpCommand(light)
switchDown = new FlipDownCommand(light)
s = new Switch()
 
s.storeAndExecute(switchUp)
s.storeAndExecute(switchDown)

Fordítás

Ez a szócikk részben vagy egészben a Command Pattern című angol Wikipédia-szócikk ezen változatának fordításán alapul. Az eredeti cikk szerkesztőit annak laptörténete sorolja fel. Ez a jelzés csupán a megfogalmazás eredetét és a szerzői jogokat jelzi, nem szolgál a cikkben szereplő információk forrásmegjelöléseként.