Oktatóanyag: Mintamegfeleltetés használata típusalapú és adatvezérelt algoritmusok létrehozásához

Olyan funkciókat írhat, amelyek úgy viselkednek, mintha más kódtárakban lévő kiterjesztett típusokat használ. A minták másik funkciója, hogy olyan funkciókat hozzon létre, amelyeket az alkalmazás igényel, és ez nem a kibővítendő típus alapvető funkciója.

Az oktatóanyag segítségével megtanulhatja a következőket:

  • Felismerhet olyan helyzeteket, amikor mintaegyezést kell használni.
  • Mintamegfeleltetési kifejezések használatával típusok és tulajdonságértékek alapján valósíthat meg viselkedést.
  • A mintaegyeztetés más technikákkal kombinálva teljes algoritmusokat hozhat létre.

Előfeltételek

Ez az oktatóanyag feltételezi, hogy ismeri a C# és a .NET használatát, beleértve a Visual Studiót vagy a .NET CLI-t is.

Forgatókönyvek mintaegyeztetéshez

A modern fejlesztés gyakran magában foglalja több forrásból származó adatok integrálását, valamint az adatokból származó információk és megállapítások egyetlen összetartó alkalmazásban való bemutatását. Ön és csapata nem fogja szabályozni vagy elérni a bejövő adatokat képviselő összes típust.

A klasszikus objektumorientált kialakítás olyan típusok létrehozását követeli meg az alkalmazásban, amelyek a több adatforrás minden adattípusát képviselik. Ezután az alkalmazás együttműködik ezekkel az új típusokkal, öröklési hierarchiákat hoz létre, virtuális metódusokat hoz létre, és absztrakciókat valósít meg. Ezek a technikák működnek, és néha ezek a legjobb eszközök. Máskor kevesebb kódot írhat. Egyértelműbb kódot írhat olyan technikákkal, amelyek elválasztják az adatokat az adatokat módosító műveletektől.

Ebben az oktatóanyagban egy olyan alkalmazást fog létrehozni és megvizsgálni, amely több külső forrásból származó bejövő adatokat vesz fel egyetlen forgatókönyvhöz. Látni fogja, hogy a mintaegyeztetés hogyan biztosít hatékony módot az adatok felhasználására és feldolgozására olyan módon, amely nem része az eredeti rendszernek.

Fontolja meg egy nagyobb nagyvárosi területet, amely az útdíjakat és a csúcsidő díjszabását használja a forgalom kezeléséhez. Olyan alkalmazást ír, amely a típusától függően számítja ki a járművek útdíjait. A későbbi fejlesztések a járműben tartózkodók száma alapján tartalmazzák a díjszabást. A további fejlesztések a hét ideje és napja alapján adnak hozzá díjszabást.

Ebből a rövid leírásból előfordulhat, hogy gyorsan felvázolt egy objektumhierarchiát a rendszer modellezéséhez. Az adatok azonban több forrásból, például más járműregisztrációs felügyeleti rendszerekből származnak. Ezek a rendszerek különböző osztályokat biztosítanak az adatok modellezéséhez, és nem rendelkezik egyetlen használható objektummodellel. Ebben az oktatóanyagban ezeket az egyszerűsített osztályokat fogja használni a külső rendszerekből származó járműadatok modellezésére, ahogyan az a következő kódban is látható:

namespace ConsumerVehicleRegistration
{
    public class Car
    {
        public int Passengers { get; set; }
    }
}

namespace CommercialRegistration
{
    public class DeliveryTruck
    {
        public int GrossWeightClass { get; set; }
    }
}

namespace LiveryRegistration
{
    public class Taxi
    {
        public int Fares { get; set; }
    }

    public class Bus
    {
        public int Capacity { get; set; }
        public int Riders { get; set; }
    }
}

A kezdőkódot a dotnet/samples GitHub-adattárból töltheti le. Láthatja, hogy a járműosztályok különböző rendszerekből származnak, és különböző névterekben vannak. Nem használható általános alaposztály.System.Object

Mintaillesztési tervek

Az oktatóanyagban használt forgatókönyv kiemeli azokat a problémákat, amelyek megoldásához a mintaegyezés megfelelő:

  • A használni kívánt objektumok nem a céljainak megfelelő objektumhierarchiában szerepelnek. Előfordulhat, hogy nem kapcsolódó rendszerek részét képező osztályokkal dolgozik.
  • A hozzáadott funkciók nem részei ezeknek az osztályoknak az alapvető absztrakciójának. A jármű által fizetett útdíj különböző típusú járművek esetében változik , de az útdíj nem a jármű alapvető funkciója.

Ha az adatok alakját és az adatokon végzett műveleteket nem írják le együtt, a C# mintamegfeleltetési funkciói megkönnyítik a munkát.

Az alapszintű útdíjszámítások implementálása

A legalapvetőbb díjszámítás csak a járműtípusra támaszkodik:

  • Az A Car 2,00 dollár.
  • A Taxi 3,50 dollár.
  • Az A Bus 5,00 dollár.
  • A DeliveryTruck 10,00 USD

Hozzon létre egy új TollCalculator osztályt, és hajtsa végre a járműtípusnak megfelelő mintát az útdíj összegének lekéréséhez. Az alábbi kód a TollCalculator.

using System;
using CommercialRegistration;
using ConsumerVehicleRegistration;
using LiveryRegistration;

namespace Calculators;

public class TollCalculator
{
    public decimal CalculateToll(object vehicle) =>
        vehicle switch
    {
        Car c           => 2.00m,
        Taxi t          => 3.50m,
        Bus b           => 5.00m,
        DeliveryTruck t => 10.00m,
        { }             => throw new ArgumentException(message: "Not a known vehicle type", paramName: nameof(vehicle)),
        null            => throw new ArgumentNullException(nameof(vehicle))
    };
}

Az előző kód egy switch olyan kifejezést használ (nem ugyanaz, mint egy switch utasítás), amely teszteli a deklarációs mintát. A kapcsolókifejezés a változóval kezdődik az vehicle előző kódban, amelyet a switch kulcsszó követ. Ezután jön az összes kapcsolókar a kapcsos kapcsos zárójelek között. A switch kifejezés további pontosításokat végez az utasítást körülvevő szintaxisra switch . A case kulcsszó nincs megadva, és az egyes karok eredménye egy kifejezés. Az utolsó két kar egy új nyelvi funkciót mutat be. Az { } eset megegyezik minden olyan nem null értékű objektummal, amely nem egyezik meg egy korábbi karnal. Ez a kar a metódusnak átadott helytelen típusokat fogja. Az { } esetnek az egyes járműtípusokra vonatkozó eseteket kell követnie. A sorrend megfordítása esetén az { } eset elsőbbséget élvezne. Végül az nullállandó minta észleli, hogy a rendszer mikor null továbbítja ezt a metódust. A null minta lehet utolsó, mert a többi minta csak a megfelelő típusú nem null objektumnak felel meg.

Ezt a kódot a következő kóddal Program.cstesztelheti:

using System;
using CommercialRegistration;
using ConsumerVehicleRegistration;
using LiveryRegistration;

using toll_calculator;

var tollCalc = new TollCalculator();

var car = new Car();
var taxi = new Taxi();
var bus = new Bus();
var truck = new DeliveryTruck();

Console.WriteLine($"The toll for a car is {tollCalc.CalculateToll(car)}");
Console.WriteLine($"The toll for a taxi is {tollCalc.CalculateToll(taxi)}");
Console.WriteLine($"The toll for a bus is {tollCalc.CalculateToll(bus)}");
Console.WriteLine($"The toll for a truck is {tollCalc.CalculateToll(truck)}");

try
{
    tollCalc.CalculateToll("this will fail");
}
catch (ArgumentException e)
{
    Console.WriteLine("Caught an argument exception when using the wrong type");
}
try
{
    tollCalc.CalculateToll(null!);
}
catch (ArgumentNullException e)
{
    Console.WriteLine("Caught an argument exception when using null");
}

Ez a kód szerepel a kezdőprojektben, de megjegyzést fűz hozzá. Távolítsa el a megjegyzéseket, és tesztelje, hogy mit írt.

Elkezdi látni, hogyan segíthetnek a minták olyan algoritmusok létrehozásában, amelyekben a kód és az adatok külön vannak. A switch kifejezés teszteli a típust, és az eredmények alapján különböző értékeket hoz létre. Ez csak a kezdet.

Bérlői díjszabás hozzáadása

Az útdíjügyi hatóság arra szeretné ösztönözni a járműveket, hogy maximális kapacitással utazzanak. Úgy döntöttek, hogy többet számolnak fel, ha a járművek kevesebb utassal rendelkeznek, és alacsonyabb díjszabással ösztönzik a teljes járműveket:

  • Az utasok nélküli autók és taxik további 0,50 dollárt fizetnek.
  • A két utassal rendelkező autók és taxik 0,50 dolláros kedvezményt kapnak.
  • A három vagy több utassal rendelkező autók és taxik 1,00 usd kedvezményt kapnak.
  • Buszok, amelyek kevesebb, mint 50%-ig teljes fizetnek egy további $ 2.00.
  • A több mint 90%-ban megtelt buszok 1,00 usd kedvezményt kapnak.

Ezek a szabályok egy tulajdonságmintával implementálhatók ugyanabban a kapcsolókifejezésben. A tulajdonságminta egy tulajdonság értékét egy állandó értékkel hasonlítja össze. A tulajdonságminta a típus meghatározása után megvizsgálja az objektum tulajdonságait. Egy eset egyetlen esete Car négy különböző esetre bővül:

vehicle switch
{
    Car {Passengers: 0} => 2.00m + 0.50m,
    Car {Passengers: 1} => 2.0m,
    Car {Passengers: 2} => 2.0m - 0.50m,
    Car                 => 2.00m - 1.0m,

    // ...
};

Az első három esetben teszteli a típust Car, majd ellenőrizze a Passengers tulajdonság értékét. Ha mindkét kifejezés egyezik, a függvény kiértékeli és visszaadja a kifejezést.

A taxis eseteket is hasonló módon bővítené:

vehicle switch
{
    // ...

    Taxi {Fares: 0}  => 3.50m + 1.00m,
    Taxi {Fares: 1}  => 3.50m,
    Taxi {Fares: 2}  => 3.50m - 0.50m,
    Taxi             => 3.50m - 1.00m,

    // ...
};

Ezután hajtsa végre a bérlői szabályokat a buszokra vonatkozó esetek kibővítésével, ahogyan az alábbi példában látható:

vehicle switch
{
    // ...

    Bus b when ((double)b.Riders / (double)b.Capacity) < 0.50 => 5.00m + 2.00m,
    Bus b when ((double)b.Riders / (double)b.Capacity) > 0.90 => 5.00m - 1.00m,
    Bus => 5.00m,

    // ...
};

Az útdíjszolgáltató nem foglalkozik a szállító teherautók utasainak számával. Ehelyett az útdíj összegét a teherautók súlyosztálya alapján az alábbiak szerint módosítják:

  • Az 5000 lbs feletti teherautókért további 5,00 usd díjat számítunk fel.
  • A 3000 lbs alatti könnyű teherautók 2,00 usd kedvezményt kapnak.

Ez a szabály a következő kóddal van implementálva:

vehicle switch
{
    // ...

    DeliveryTruck t when (t.GrossWeightClass > 5000) => 10.00m + 5.00m,
    DeliveryTruck t when (t.GrossWeightClass < 3000) => 10.00m - 2.00m,
    DeliveryTruck => 10.00m,
};

Az előző kód egy when kapcsolókar záradékát mutatja. A záradékot a when tulajdonság egyenlőségétől eltérő feltételek tesztelésére használhatja. Ha végzett, egy olyan metódussal fog rendelkezni, amely az alábbi kódhoz hasonlóan néz ki:

vehicle switch
{
    Car {Passengers: 0}        => 2.00m + 0.50m,
    Car {Passengers: 1}        => 2.0m,
    Car {Passengers: 2}        => 2.0m - 0.50m,
    Car                        => 2.00m - 1.0m,

    Taxi {Fares: 0}  => 3.50m + 1.00m,
    Taxi {Fares: 1}  => 3.50m,
    Taxi {Fares: 2}  => 3.50m - 0.50m,
    Taxi             => 3.50m - 1.00m,

    Bus b when ((double)b.Riders / (double)b.Capacity) < 0.50 => 5.00m + 2.00m,
    Bus b when ((double)b.Riders / (double)b.Capacity) > 0.90 => 5.00m - 1.00m,
    Bus => 5.00m,

    DeliveryTruck t when (t.GrossWeightClass > 5000) => 10.00m + 5.00m,
    DeliveryTruck t when (t.GrossWeightClass < 3000) => 10.00m - 2.00m,
    DeliveryTruck => 10.00m,

    { }     => throw new ArgumentException(message: "Not a known vehicle type", paramName: nameof(vehicle)),
    null    => throw new ArgumentNullException(nameof(vehicle))
};

Ezek közül a kapcsolókarok közül sok rekurzív minták példája. Például Car { Passengers: 1} egy tulajdonságmintán belüli állandó mintát jelenít meg.

Ezt a kódot kevésbé ismétlődővé teheti beágyazott kapcsolók használatával. Taxi Az Car előző példákban négy különböző kar található. Mindkét esetben létrehozhat egy deklarációs mintát, amely állandó mintába kerül. Ez a technika a következő kódban jelenik meg:

public decimal CalculateToll(object vehicle) =>
    vehicle switch
    {
        Car c => c.Passengers switch
        {
            0 => 2.00m + 0.5m,
            1 => 2.0m,
            2 => 2.0m - 0.5m,
            _ => 2.00m - 1.0m
        },

        Taxi t => t.Fares switch
        {
            0 => 3.50m + 1.00m,
            1 => 3.50m,
            2 => 3.50m - 0.50m,
            _ => 3.50m - 1.00m
        },

        Bus b when ((double)b.Riders / (double)b.Capacity) < 0.50 => 5.00m + 2.00m,
        Bus b when ((double)b.Riders / (double)b.Capacity) > 0.90 => 5.00m - 1.00m,
        Bus b => 5.00m,

        DeliveryTruck t when (t.GrossWeightClass > 5000) => 10.00m + 5.00m,
        DeliveryTruck t when (t.GrossWeightClass < 3000) => 10.00m - 2.00m,
        DeliveryTruck t => 10.00m,

        { }  => throw new ArgumentException(message: "Not a known vehicle type", paramName: nameof(vehicle)),
        null => throw new ArgumentNullException(nameof(vehicle))
    };

Az előző mintában egy rekurzív kifejezés használata azt jelenti, hogy nem ismétli meg a Car tulajdonság értékét tesztelő gyermekkarokat tartalmazó karokat és Taxi karokat. Ezt a technikát nem használják a Bus fegyverekhez, DeliveryTruck mert ezek a fegyverek a tulajdonság tesztelési tartományai, nem pedig különálló értékek.

Csúcsárak hozzáadása

A végső funkcióhoz az útdíjszolgáltató időérzékeny csúcsárat szeretne hozzáadni. A reggeli és az esti csúcsidőben az útdíjak megduplázódnak. Ez a szabály csak egy irányban befolyásolja a forgalmat: reggel befelé a városba, és kifelé az esti csúcsidőben. A munkanap más időszakaiban az útdíjak 50%-kal növekednek. Késő este és kora reggel az útdíj 25%-kal csökken. A hétvégén ez a normál ráta, az időtől függetlenül. Ezt a következő kóddal fejezheti ki egy sor if és else utasítás használatával:

public decimal PeakTimePremiumIfElse(DateTime timeOfToll, bool inbound)
{
    if ((timeOfToll.DayOfWeek == DayOfWeek.Saturday) ||
        (timeOfToll.DayOfWeek == DayOfWeek.Sunday))
    {
        return 1.0m;
    }
    else
    {
        int hour = timeOfToll.Hour;
        if (hour < 6)
        {
            return 0.75m;
        }
        else if (hour < 10)
        {
            if (inbound)
            {
                return 2.0m;
            }
            else
            {
                return 1.0m;
            }
        }
        else if (hour < 16)
        {
            return 1.5m;
        }
        else if (hour < 20)
        {
            if (inbound)
            {
                return 1.0m;
            }
            else
            {
                return 2.0m;
            }
        }
        else // Overnight
        {
            return 0.75m;
        }
    }
}

Az előző kód helyesen működik, de nem olvasható. Az összes bemeneti esetet és beágyazott utasítást if át kell láncra fűznie a kód okához. Ehelyett a funkcióhoz mintaegyezést fog használni, de más technikákkal is integrálhatja. Létrehozhat egyetlen mintaegyezés-kifejezést, amely az irány, a hét napja és az idő összes kombinációját figyelembe veszi. Az eredmény egy bonyolult kifejezés lenne. Nehéz lenne olvasni és nehéz megérteni. Ez megnehezíti a helyesség biztosítását. Ehelyett kombinálja ezeket a módszereket, hogy olyan értékeket hozzon létre, amelyek tömören ismertetik ezeket az állapotokat. Ezután használja a mintaegyezést az útdíj szorzójának kiszámításához. A rekord három különálló feltételt tartalmaz:

  • A nap egy hétköznap vagy egy hétvége.
  • Az útdíj beszedésének idősávja.
  • Az irány a városba vagy a városon kívül van

Az alábbi táblázat a bemeneti értékek és a csúcsdíj-szorzó kombinációit mutatja be:

Nap Idő Irány Prémium
Weekday reggeli rohanás Bejövő x 2.00
Weekday reggeli rohanás kimenő x 1,00
Weekday Nappali Bejövő x 1,50
Weekday Nappali kimenő x 1,50
Weekday esti rohanás Bejövő x 1,00
Weekday esti rohanás kimenő x 2.00
Weekday Egyik napról Bejövő x 0,75
Weekday Egyik napról kimenő x 0,75
Hétvége reggeli rohanás Bejövő x 1,00
Hétvége reggeli rohanás kimenő x 1,00
Hétvége Nappali Bejövő x 1,00
Hétvége Nappali kimenő x 1,00
Hétvége esti rohanás Bejövő x 1,00
Hétvége esti rohanás kimenő x 1,00
Hétvége Egyik napról Bejövő x 1,00
Hétvége Egyik napról kimenő x 1,00

A három változónak 16 különböző kombinációja van. Néhány feltétel kombinálásával egyszerűsítheti a végső kapcsolókifejezést.

Az útdíjakat gyűjtő rendszer egy struktúrát DateTime használ az útdíj beszedésének idejére. Az előző tábla változóit létrehozó tagmetelyek létrehozása. Az alábbi függvény egy mintaegyező kapcsolókifejezéssel fejezi ki, hogy egy DateTime hétvége vagy egy hétköznap van-e:

private static bool IsWeekDay(DateTime timeOfToll) =>
    timeOfToll.DayOfWeek switch
    {
        DayOfWeek.Monday    => true,
        DayOfWeek.Tuesday   => true,
        DayOfWeek.Wednesday => true,
        DayOfWeek.Thursday  => true,
        DayOfWeek.Friday    => true,
        DayOfWeek.Saturday  => false,
        DayOfWeek.Sunday    => false
    };

Ez a módszer helyes, de ismétlődő. Egyszerűsítheti azt, ahogy az a következő kódban is látható:

private static bool IsWeekDay(DateTime timeOfToll) =>
    timeOfToll.DayOfWeek switch
    {
        DayOfWeek.Saturday => false,
        DayOfWeek.Sunday => false,
        _ => true
    };

Ezután adjon hozzá egy hasonló függvényt, amely kategorizálja az időt a blokkokba:

private enum TimeBand
{
    MorningRush,
    Daytime,
    EveningRush,
    Overnight
}

private static TimeBand GetTimeBand(DateTime timeOfToll) =>
    timeOfToll.Hour switch
    {
        < 6 or > 19 => TimeBand.Overnight,
        < 10 => TimeBand.MorningRush,
        < 16 => TimeBand.Daytime,
        _ => TimeBand.EveningRush,
    };

Adjon hozzá egy privát enum értéket az egyes időtartományok különálló értékké alakításához. Ezután a GetTimeBand metódus relációs mintákat és kötőhártya-mintákat orhasznál. A relációs minta lehetővé teszi számértékek <tesztelését a , >, <=vagy >=. A or minta ellenőrzi, hogy egy kifejezés megfelel-e egy vagy több mintának. Mintával and azt is ellenőrizheti, hogy egy kifejezés két különböző mintával egyezik-e, és not hogy egy kifejezés nem egyezik-e a mintával.

Miután létrehozta ezeket a metódusokat, használhat egy másik switch kifejezést a rekordmintával a díjszabási prémium kiszámításához. Létrehozhat egy switch kifejezést mind a 16 karral:

public decimal PeakTimePremiumFull(DateTime timeOfToll, bool inbound) =>
    (IsWeekDay(timeOfToll), GetTimeBand(timeOfToll), inbound) switch
    {
        (true, TimeBand.MorningRush, true) => 2.00m,
        (true, TimeBand.MorningRush, false) => 1.00m,
        (true, TimeBand.Daytime, true) => 1.50m,
        (true, TimeBand.Daytime, false) => 1.50m,
        (true, TimeBand.EveningRush, true) => 1.00m,
        (true, TimeBand.EveningRush, false) => 2.00m,
        (true, TimeBand.Overnight, true) => 0.75m,
        (true, TimeBand.Overnight, false) => 0.75m,
        (false, TimeBand.MorningRush, true) => 1.00m,
        (false, TimeBand.MorningRush, false) => 1.00m,
        (false, TimeBand.Daytime, true) => 1.00m,
        (false, TimeBand.Daytime, false) => 1.00m,
        (false, TimeBand.EveningRush, true) => 1.00m,
        (false, TimeBand.EveningRush, false) => 1.00m,
        (false, TimeBand.Overnight, true) => 1.00m,
        (false, TimeBand.Overnight, false) => 1.00m,
    };

A fenti kód működik, de egyszerűsíthető. Mind a nyolc kombináció a hétvégén ugyanazzal az útdíj. A nyolcat a következő sorra cserélheti:

(false, _, _) => 1.0m,

A bejövő és a kimenő forgalom is ugyanazt a szorzót használja a hétköznap napközben és az éjszakai órákban. Ez a négy kapcsolókar a következő két sorra cserélhető:

(true, TimeBand.Overnight, _) => 0.75m,
(true, TimeBand.Daytime, _)   => 1.5m,

A kódnak a következő kódhoz hasonlóan kell kinéznie a két módosítás után:

public decimal PeakTimePremium(DateTime timeOfToll, bool inbound) =>
    (IsWeekDay(timeOfToll), GetTimeBand(timeOfToll), inbound) switch
    {
        (true, TimeBand.MorningRush, true)  => 2.00m,
        (true, TimeBand.MorningRush, false) => 1.00m,
        (true, TimeBand.Daytime,     _)     => 1.50m,
        (true, TimeBand.EveningRush, true)  => 1.00m,
        (true, TimeBand.EveningRush, false) => 2.00m,
        (true, TimeBand.Overnight,   _)     => 0.75m,
        (false, _,                   _)     => 1.00m,
    };

Végül eltávolíthatja a normál árat fizető két csúcsidőt. Miután eltávolította ezeket a karokat, lecserélheti az false eldobott (_) az utolsó kapcsolókarban. A következő kész metódust fogja használni:

public decimal PeakTimePremium(DateTime timeOfToll, bool inbound) =>
    (IsWeekDay(timeOfToll), GetTimeBand(timeOfToll), inbound) switch
    {
        (true, TimeBand.Overnight, _) => 0.75m,
        (true, TimeBand.Daytime, _) => 1.5m,
        (true, TimeBand.MorningRush, true) => 2.0m,
        (true, TimeBand.EveningRush, false) => 2.0m,
        _ => 1.0m,
    };

Ez a példa a mintaegyeztetés egyik előnyét emeli ki: a mintaágak kiértékelése sorrendben történik. Ha úgy rendezi át őket, hogy egy korábbi ág kezelje az egyik későbbi esetet, a fordító figyelmezteti a nem elérhető kódra. Ezek a nyelvi szabályok egyszerűbbé tették az előző egyszerűsítéseket azzal a magabiztossággal, hogy a kód nem változott.

A mintaegyeztetés bizonyos kódtípusokat olvashatóbbá tesz, és alternatívát kínál az objektumorientált technikák helyett, ha nem tud kódot hozzáadni az osztályokhoz. A felhő miatt az adatok és a funkciók egymástól elkülönülnek. Az adatok alakját és a rajta lévő műveleteket nem feltétlenül írják le együtt. Ebben az oktatóanyagban a meglévő adatokat teljesen más módon használta fel, mint az eredeti függvényét. A mintamegfeleltetés lehetővé tette, hogy olyan funkciókat írjon, amelyek felülírják ezeket a típusokat, még akkor is, ha nem tudta kiterjeszteni őket.

Következő lépések

A kész kódot letöltheti a dotnet/samples GitHub-adattárból. Saját maga is megismerheti a mintákat, és hozzáadhatja ezt a technikát a szokásos kódolási tevékenységekhez. Tanulás ezekkel a technikákkal más módon is megközelítheti a problémákat, és új funkciókat hozhat létre.

Lásd még