Software bouwen

Vaak kunnen verschillende algoritmen of implementaties dezelfde taak correct oplossen. De code die u kiest, brengt een ontwerp over. Ontwikkelaars en agents die code schrijven die uw code lezen, gebruiken de structuur ervan om uw intentie af te leiden: wat elke bewerking nodig heeft, waar deze hoort en wanneer deze wordt uitgevoerd.

Tip

Dit artikel maakt deel uit van de sectie Grondbeginselen voor ontwikkelaars die basis-C#-expressies, -instructies en -methoden kennen. Als u nieuw bent in programmeren, begint u eerst met de Aan de slag-handleidingen.

In deze sectie worden geldige benaderingen voor algoritmen vergeleken en worden subtiele verschillen in hun gedrag en in de manier waarop lezers deze interpreteren uitgelegd. U combineert vertrouwde taalelementen, zoals typen, expressies, instructies en methoden, en kiest vormen die het door u beoogde ontwerp uitdrukken.

Gedrag en ontwerpintentie vergelijken

Voordat u een implementatie kiest, moet u overwegen hoe deze door andere code wordt gebruikt. Deze code is de aanroeper: het levert invoer, roept uw bewerking aan en gebruikt de resultaten. Heeft het een voltooid resultaat onmiddellijk nodig of kan het waarden één voor één verwerken? Maakt een helper alleen deel uit van deze bewerking, of zullen andere bewerkingen deze ook gebruiken? Deze vragen helpen u bij het vergelijken van zowel het gedrag dat u nodig hebt als het ontwerp dat uw code uitdrukt.

Een korte inline-bewerking toont lezers de stappen op de plaats van gebruik. Where(task => task.Priority > 3) maakt de filterregel bijvoorbeeld direct zichtbaar. Een benoemde helper, zoals in Where(IsUrgent), benadrukt het doel van de regel en laat lezers de details afzonderlijk inspecteren. Kies afhankelijk van de vraag of de stappen of hun doel de omringende code het beste uitleggen.

Waar u een helper declareert, communiceert de beoogde scope. Een lokale functie binnen ShowDashboard geeft aan dat de helper deel uitmaakt van die bewerking en voorkomt dat andere leden deze aanroepen. Met een privémethode op het omvattende type wordt de helper beschikbaar gemaakt voor andere leden, wat aangeeft dat u ergens anders in het type hergebruik verwacht. Beide kunnen dezelfde berekening structureren, maar ze wekken bij lezers verschillende verwachtingen over de rol ervan.

Hoe u de invoer van een helper aanlevert, maakt de afhankelijkheden duidelijk. Door een limiet als parameter door te geven, zoals IsUrgent(task, minimumPriority), wordt die parameter zichtbaar bij elke aanroep. Een lokale functie of lambda kan in plaats daarvan capturenminimumPriority, met behulp van de variabele uit het omringende bereik zonder deze als argument te ontvangen. Capture verbindt de helper met de context van de bevatde bewerking; expliciete parameters maken de opgegeven gegevens gemakkelijker te identificeren. Gebruik een static lokale functie of lambda als u wilt afdwingen dat de omringende status niet wordt vastgelegd.

De manier waarop u resultaten retourneert, geeft aan wanneer het werk plaatsvindt. Een methode waarmee een verzameling wordt gemaakt en geretourneerd, voltooit dat werk voordat de aanroeper de verzameling ontvangt. Een iterator produceert elementen terwijl de aanroepende code deze aanvraagt, zodat de aanroepende code kan stoppen voordat alle elementen worden geproduceerd. Beide kunnen dezelfde waarden produceren, maar aanroepers moeten weten of ze voltooide resultaten ontvangen of een reeks waarvan de berekening nog moet plaatsvinden.

Identificeer voor elke keuze eerst het gedrag dat de aanroepende code nodig heeft. Bedenk vervolgens wat een andere ontwikkelaar of coderingsagent zal afleiden uit de naam, het bereik, de invoer en het resultaat. Kies een vorm waarmee deze verwachtingen overeenkomen met uw ontwerp en leg belangrijk gedrag uit dat niet alleen uit de vorm van de code blijkt. Het doel is om een weloverwogen keuze te maken tussen geldige alternatieven, niet om overal één vorm toe te passen.

Lees over ontwerpkeuzes aan de hand van een voorbeeld

In het volgende voorbeeld ziet u hoe verschillende keuzes verschillende onderdelen van een ontwerp communiceren. Er worden taken weergegeven die dringend zijn en klaar zijn om te beginnen. Een ProjectTask bevat de titel, prioriteit en geblokkeerde status van een taak.

var tasks = new[]
{
    new ProjectTask("Confirm venue", Priority: 3, IsBlocked: false),
    new ProjectTask("Print badges", Priority: 2, IsBlocked: true),
    new ProjectTask("Test microphones", Priority: 2, IsBlocked: false),
};

ShowDashboard("Community meetup", tasks, minimumPriority: 2);

static void ShowDashboard(
    string projectName,
    IEnumerable<ProjectTask> tasks,
    int minimumPriority)
{
    var urgentTasks = tasks.Where(
        task => task.Priority >= minimumPriority);

    foreach (ProjectTask task in ReadyTasks(urgentTasks))
    {
        Console.WriteLine(FormatTask(task));
    }

    string FormatTask(ProjectTask task) =>
        $"{projectName}: {task.Title}";
}

static IEnumerable<ProjectTask> ReadyTasks(
    IEnumerable<ProjectTask> tasks)
{
    foreach (ProjectTask task in tasks)
    {
        if (!task.IsBlocked)
        {
            yield return task;
        }
    }
}

record ProjectTask(string Title, int Priority, bool IsBlocked);

Elke keuze houdt een ander deel van het werk duidelijk:

  • De lambda die is doorgegeven aan Where plaatst een korte prioriteitsregel naast de bewerking die deze gebruikt.
  • De lokale functie FormatTask duidt een opmaakstap aan die alleen bij ShowDashboard hoort.
  • Met de iterator ReadyTasks kunnen aanroepers gereedstaande taken verwerken zonder een tussenliggende verzameling te maken.
  • De foreach lus maakt de laatste actie, het schrijven van elk resultaat, expliciet.

Ter vergelijking zou Where(static task => !task.IsBlocked)ReadyTasks kunnen vervangen, omdat deze gereedheidstest een eenvoudig filter is. Beide vormen stellen het filter uit tot de enumeratie. De lambda plaatst de regel op de oproepsite; ReadyTasks geeft de regel een naam. Kies ertussen op basis van welke informatie lezers helpt de beller te begrijpen.

In de artikelen die volgen, worden vergelijkingen zoals deze gebruikt om uit te leggen wanneer elke benadering past en wat lezers uit uw keuze kunnen afleiden.

Volgende stappen