Strategy „Definujte rodinu algoritmů, zapouzdřuje je aby byly vzájemně zaměnitelné. Strategie umožňuje, aby se algoritmus nebyl závislý na klientech, kteří jej používají.“ 1
Motivace - Takhle to může začít Ještě není třeba implementovat strategy public Output RunScheduler( string algorithm, Input parameter) { if (algorithm == "HOCF" || algorithm == "LOCF") return LOCForHOCF(parameter); if (algorithm == "HOCFv2" || algorithm == "LOCFv2") { var preprocessed = LOCForHOCFv2_preprocess(parameter); return LOCForHOCFv2(preprocessed); } return null; } public Output LOCForHOCF(Input parameter) { /*…*/} public Output LOCForHOCFv2(InputPrep prep) public InputPrep LOCForHOCFv2_preprocess (Input parameter)
Motivace – Takhle to pokračuje Přidám další... Je to jen jedna větev public Output LOCForHOCF(Input parameter) { /*…*/} public Output LOCForHOCFv2(InputPrep prep) public InputPrep LOCForHOCFv2_preprocess (Input parameter) public Output MILP (InputPrep2 prep) public InputPrep2 MILP_preprocess(Input parameter) public Output RunScheduler(string algorithm, Input parameter) { if (algorithm == "HOCF" || algorithm == "LOCF") return LOCForHOCF(parameter); if (algorithm == "HOCFv2" || algorithm == "LOCFv2") { var preprocessed = LOCForHOCFv2_preprocess(parameter); return LOCForHOCFv2(preprocessed); } if (algorithm == "MILP" || algorithm == "LP_Heuristic") var preprocessed = MILP_preprocess(parameter); return MILP(preprocessed); } return null;
Vlastnosti takového kódu Komplexní kód Špatná čitelnost Těžká správa kódu Těžka rozšiřitelnost Důsledky Zkrátka to porušuje
Motivace – Teď už je čas to změnit ...za nějaký čas v TFS
Motivace – Případně už od začátku „Chceme, aby aplikace dělala to a tamto, ale ješte to není úplně jisté a může se to v budoucnu změnit…“
Strategy pattern Strategy - behavioral pattern Vhodné když Používáme více algoritmů nad stejnými daty Máme různé implementace téhož algoritmu Chceme měnit chování dynamicky Hromada kódu v mnoha podmínkách Zapouzdřuje algoritmy ze společné rodiny Open/closed principle Otevřená pro rozšíření, uzavřená pro modifikaci Vyhýbat se abstraktním třídám
Struktura Účastníci Strategy ConcreteStrategy Context Interface, definuje rozhraní společné všem algoritmům ConcreteStrategy Implementace konkrétního algoritmu - strategie Context Je mu nastaven ConcreteStrategy objekt Spravuje referenci na Strategy objekt Může zpřístupňovat data/API konkrétním ConcreteStrategy objektům
Strategy Jak z toho ven? public interface IAlgorithm { ResultClass RunAlgorithm(InputClass input); } public class HOCF : IAlgorithm { CustomInputClass preprocess(InputClass input) {/*preprocessing*/} public OutputClass RunAlgorithm(InputClass input) { var processedInput = preprocess(input); /*compute what needs to be computed*/ return result; } } public class Scheduler : IScheduler{ public OutputClass runScheduler (string algName, InputClass input){ var processedInput = AlgFactory.Get(algName); return Algorithm.RunAlgorithm(input); } }
Strategy Ještě trochu lépe (Dependency injection) public class Scheduler : IScheduler{ /*dovoluje změnit strategii za běhu*/ public IAlgorithm Algorithm {get; set;} public Shceduler(IAlgorithm alg){ Algorithm = alg; } public OutputClass runScheduler(InputClass input){ return Algorithm.RunAlgorithm(input); } } public class Caller{ private IAlgorithmFactory _algorithmFactory; private IScheduler _scheduler; public Scheduler(IAlgorithmFactory algFac , IScheduler sched){ /*nastavení fieldů*/ } public OutputClass Do(string algorithmName, InputClass input){ var alg = _algorithmFactory.Get(algorithmName); _scheduler=new Scheduler(alg); return _scheduler.RunScheduler(input); } }
Implementace – Chováni jednotek Rodina strategií public interface IDifficulty { void pickAction(ContextClass context); } public class EasyDifficulty : IDifficulty { public void pickAction(ContextClass context){ //wait patiently untill defeated public class HardDifficulty : IDifficulty { //fight for every inch public class NightmareDiff : IDifficulty { public void pickAction(ContextClass context) { //cheat for unlimited resources }
Implementace – příklad (C#) Kontext public class Enemy : IEnemy{ public IDifficulty Difficulty { get; set; } public void GameTurn(){ //... Difficulty.PickAction(context); //... } }
Implementace – příklad (C#) Klientský kód public void Game() { IEnemy terran = new Enemy(); terran.Difficulty = new EasyDifficulty(); IEnemy zerg = new Enemy(); zerg.Difficulty = new HardDifficulty(); terran.GameTurn(); \\terran-easy , zerg.GameTurn(); \\zerg hard terran.Difficulty=new NightmareDiff(); zerg.Difficulty = new EasyDifficulty(); terran.GameTurn(); \\ terran nightmare zerg.GameTurn(); \\zerg easy } Určuje Strategii Kontextu Operuje jenom na Kontextu
Implementace Jak předat kontext strategii? Předání kontextových dat v parametrech metody Nemusí být použity Můžu jich být hodně. Přistupuje jen k datům, které potřebuje Rozhraní a kontext musí být dobře navržené. Strategie může být pro kontextovou třídu volitelná Defaultní Strategie Pokud uživatel nechce, nemusí nic nastavovat
Vlastnosti Výhody Nevýhody Zapouzdření algoritmu Převod výběru algoritmu (switch/if) na dědičnost Rozšiřitelnost chování Konkrétní chování lze určit za běhu Nevýhody Overhead Klient musí umět vybrat strategii
Aplikace Použití v praxi GUI Třídění Komprese dat Obecně Validace formulářových prvků Layout manager – různé algoritmy pro layout prvků Třídění Možnost vybírat strategii třídění dle okolností Komprese dat Různé metody komprese dat Obecně Nahrávání, ukládání a generování výsledků v různých formátech Operace výstupu (konzole, soubor, síť…)
Související návrhové vzory State Chování určeno stavem Stav se může měnit implicitně, Strategii mění uživatel Command Chování zapouzdřeno v objektu
KONEC Pro více informací Dependency injeciton - https://msdn.microsoft.com/en-us/library/ff921152.aspx Tfs – https://en.wikipedia.org/wiki/Team_Foundation_Server C# Properties - https://docs.microsoft.com/en-us/dotnet/articles/csharp/programming-guide/classes-and-structs/properties