Stratégia programtervezési minta

From Wikipedia, the free encyclopedia

Remove ads

A számítógép-programozásban a stratégia minta (vezérelv mintaként is ismert) egy szoftvertervezési minta, amely lehetővé teszi, hogy egy algoritmus viselkedését a futás során válasszuk meg. A stratégia minta meghatározza az algoritmusok egy családját, egységbe foglal minden algoritmust, és a családon belül cserélhetővé teszi ezeket az algoritmusokat. A stratégia segítségével az algoritmus az őt használó kliensektől függetlenül változhat, miután az megtette a beállításokat. A stratégia minta az egyike a Design Patterns könyvben[1] szereplő mintáknak, mely népszerűsítette a minták használatát a szoftvertervezés folyamatában.

Például egy osztály, mely bejövő adatokat érvényesít, stratégiai mintát használhat a validációs algoritmus kiválasztására a bejövő adat típusa, az adat forrása, felhasználói választás vagy más megkülönböztető tényező alapján. Ezek a tényezők nem minden esetben ismertek a futtatás előtt, és szükségessé válhat radikálisan eltérő érvényesítési módok használata. A validációs stratégiákat, melyeket külön egységbe zártunk a validáló objektumoktól, a rendszer más területéről származó validációs objektumok (vagy akár más rendszerek) is használhatják kódduplikáció nélkül.

A programozási nyelvekben alapvető követelmény, hogy képes legyen az adatstruktúrában található kódra mutató referenciák tárolására és elérésére. Ennek elérését olyan mechanizmusok segítik, mint a natív függvény pointerek, elsődleges típusú függvények, osztályok vagy osztály példányok az objektumorientált programozási nyelvekben, vagy mint a nyelv implementációs belső tárolójának elérése önelemzés (reflexió, reflection) segítségével.

Remove ads

Felépítése

Thumb
Stratégia tervezési minta UML ábrája
Thumb
Stratégia tervezési minta LePUS3-ban (legend)

Példa

C#

namespace IVSR.Designpattern.Strategy {
    //A stratégiák interfésze
    public interface ICalculateInterface {
       int Calculate(int value1, int value2);
    }

    //stratégiák
    //Stratégia 1: Mínusz
    class Minus : ICalculateInterface {
        public int Calculate(int value1, int value2) {
            return value1 - value2;
        }
    }

    //Stratégia 2: Plusz
    class Plus : ICalculateInterface {
        public int Calculate(int value1, int value2) {
            return value1 + value2;
        }
    }

    //A kliens
    class CalculateClient {
        private ICalculateInterface calculateStrategy;

        //Konstruktor: az interfészhez rendeli a stratégiá
        public CalculateClient(ICalculateInterface strategy) {
            this.calculateStrategy = strategy;
        }

        //Stratégia végrehajtása
        public int Calculate(int value1, int value2) {
            return calculateStrategy.Calculate(value1, value2);
        }
    }

    //Inicializálás
    protected void Page_Load(object sender, EventArgs e) {
        CalculateClient minusClient = new CalculateClient(new Minus());
        Response.Write("<br />Minus: " + minusClient.Calculate(7, 1).ToString());

        CalculateClient plusClient = new CalculateClient(new Plus());
        Response.Write("<br />Plus: " + plusClient.Calculate(7, 1).ToString());
    }
}

Java

A következő példa Java programozási nyelvben íródott.

package javaapplication8;

import java.util.ArrayList;
import java.util.List;

 // Minta a Stratégia tervezési mintára
public class JavaApplication8 {
    public static void main(String[] args){
        Customer a = new Customer();
        a.setStrategy(new NormalStrategy());
        
        // Normál számlázás
        a.add(1.2,1);
        
        // Happy Hour inditása
        a.setStrategy(new HappyHourStrategy());
        a.add(1.2,2);
        
        // Új ügyfél
        Customer b = new Customer();
        b.setStrategy(new HappyHourStrategy());
        b.add(0.8,1);
        // Az ügyfél fizet
        a.printBill();

        // Happy Hour vége
        b.setStrategy(new NormalStrategy());
        b.add(1.3,2);
        b.add(2.5,1);
        b.printBill();
        
    }
}

class Customer{
    private List<Double> drinks;
    private BillingStrategy strategy;

    public Customer() {
        this.drinks = new ArrayList<Double>();
    }

    // Új elem az étlapra
    public void add(double price, int quantity){
        drinks.add(price*quantity);
    }

     // Számla kifizetése
    public void printBill(){
        System.out.println("Total due: " + strategy.sum(drinks));
        drinks.clear();
    }

     // Stratégia beállítása
    public void setStrategy(BillingStrategy strategy) {
        this.strategy = strategy;
    }

}

interface BillingStrategy{
     // A fogyasztott italok összegzett ára
    public double sum(List<Double> drinks);
}

// Normál számlátási stratégia (változatlan árakon)
class NormalStrategy implements BillingStrategy{
    public double sum(List<Double> drinks) {
        double sum = 0;
        for(Double i : drinks){
            sum += i;
        }
        return sum;
    }
}

 // Happy hour stratégia(10% engedmény)
class HappyHourStrategy implements BillingStrategy{
    public double sum(List<Double> drinks) {
        double sum = 0;
        for(Double i : drinks){
            sum += i;
        }
        return sum*0.9;
    }
}

Egy sokkal egyszerűbb példa a 'modern Javában' (Java 8 vagy újabb) a lambdák használata[2]

Remove ads

Stratégia és a nyílt-zárt alapelv

Thumb
Gyorsulás és fék viselkedések minden egyes új auto modellben deklarálva vannak.

A stratégia minta szerint az osztály viselkedését nem szabad örökítenünk. Ehelyett egységbe kell zárnunk interfészek alkalmazásával. Például vegyünk egy Autó osztályt. Az Autó két lehetséges funkciója a fék és a gyorsulás.

Mivel a gyorsulás és a fék viselkedése gyakran eltérő a különböző modellek esetén, az általános felfogás szerint ezeket a viselkedéseket külön osztályokba sorolnánk, amelyek ugyanazt az interfészt valósítják meg, a kód öröklésének kiiktatásával. Ennek a megközelítésnek viszont jelentős hátrányai vannak: minden egyes új autómodellhez újra kell értelmezni a gáz és a fék viselkedését. Ezen viselkedések kezelésével járó munka a modellek számának a növekedésével jelentősen megnő, és kód-duplikációhoz vezethet. Továbbá nem egyszerű feladat meghatározni minden modell pontos viselkedését anélkül, hogy a bennük megírt kódot külön megvizsgálnunk.

A stratégia minta öröklés helyett kompozíciót használ. Itt a viselkedést különálló interfészként és specifikus osztályokként definiáljuk, melyek ezeket az interfészeket valósítják meg. Ez lehetővé teszi a viselkedés és a viselkedést alkalmazó osztály jobb szétválasztását. A viselkedést anélkül tudjuk megváltoztatni, hogy betörnénk az osztályt, ami használja, illetve az osztályok lecserélhetik viselkedésüket a konkrét megvalósítás megváltoztatása által, anélkül hogy a kódot jelentőségteljesen megváltoztatnánk. A viselkedés változhat futási időben és tervezési időben is. Például az autó objektum fék viselkedését megváltoztathatjuk BrakeWithABS() -ről Brake() -re azáltal, hogy megváltoztatjuk a brakeBehavior tagot az alábbi módon:

brakeBehavior = new Brake();
/* Algoritmusok egységbezárt családja
 * Interfész és annak megvalósításai
 */
public interface IBrakeBehavior {
    public void brake();
}

public class BrakeWithABS implements IBrakeBehavior {
    public void brake() {
        System.out.println("Brake with ABS applied");
    }
}

public class Brake implements IBrakeBehavior {
    public void brake() {
        System.out.println("Simple Brake applied");
    }
}

 
/* Kliens mely a fenti algoritmusokat váltakozva használhatja */
public abstract class Car {
    protected IBrakeBehavior brakeBehavior;

    public void applyBrake() {
        brakeBehavior.brake();
    }

    public void setBrakeBehavior(IBrakeBehavior brakeType) {
        this.brakeBehavior = brakeType;
    }
}

/* 1. Kliens mely a fenti Bake algoritmust használja konstruktorban */
public class Sedan extends Car {
    public Sedan() {
        this.brakeBehavior = new Brake();
    }
}

/* 2. Kliens mely a BrakeWithABS algoritmust használja konstruktorban */
public class SUV extends Car {
    public SUV() {
        this.brakeBehavior = new BrakeWithABS();
    }
}

/* Használunk egy PéldaAutót (carExample) */
public class CarExample {
    public static void main(String[] args) {
        Car sedanCar = new Sedan();
        sedanCar.applyBrake();  // "Brake" osztály meghívása
 
        Car suvCar = new SUV();
        suvCar.applyBrake();    // "BrakeWithABS" osztály meghívása
 
        // fék viselkedésének dinamikus beallítása
        suvCar.setBrakeBehavior( new Brake() );
        suvCar.applyBrake();    // "Brake" osztály meghívása
    }
}

Ez a megoldás nagyobb rugalmasságot biztosít és összhangban van a Nyitva-Zárt Alapelvvel, mely kimondja, hogy az osztályoknak nyitottnak kell lenni a kiterjesztésekre, de zártnak a módosításokra.

Remove ads

Források

További információk

Fordítás

Loading related searches...

Wikiwand - on

Seamless Wikipedia browsing. On steroids.

Remove ads