Programová organizace

Návod

Začínáte s vývojem softwaru? Začněte nejprve kurzy Začínáme . S tím, jak vaše projekty rostou, se dozvíte o organizaci programů přirozeně.

Máte zkušenosti v jiném jazyce? Pokud jste obeznámeni s řešeními a projekty v sadě Visual Studio nebo buildovacími systémy, jako je Maven nebo Cargo, tento článek tyto koncepty mapuje na .NET.

S rostoucím růstem aplikace jazyka C# je potřeba kód uspořádat. .NET poskytuje hierarchii organizačních nástrojů – řešení, projektů, sestavení, oborů názvů a typů – které spolupracují, aby bylo možné spravovat velké základy kódu. Zde popsané konvence představují široký konsensus v komunitě .NET. Můžete se odhodovat z konkrétních důvodů, ale následující konvence váš kód zpřístupňuje a umožňuje navigaci ostatním vývojářům .NET.

Organizační hierarchie

Uspořádejte typickou aplikaci .NET ve vrstvách od nejširšího po nejvýraznější:

Úroveň Description Příklad
Řešení Kontejner, který seskupuje související projekty. MyApp.slnx
Projekt Modul, který vytváří jednu sestavu. MyApp.Web.csproj
Sestavení Soubor .dll nebo .exe, který je zkompilován projektem. MyApp.Web.dll
Namespace Logické seskupení typů. MyApp.Web.Controllers
Type Třída, struktura, rozhraní, výčet nebo delegát. OrderController

Každá úroveň slouží k jinému účelu. Řešení uspořádají pracovní postup vývoje. Projekty definují, co se zkompiluje dohromady, a každý projekt vytvoří jedno sestavení. Sestavení jsou jednotkou nasazení a správy verzí. Jmenné prostory zabraňují kolizím názvů a usnadňují nalezení typů. Jedno sestavení může obsahovat více oborů názvů a jeden obor názvů může zahrnovat více sestavení. Typy definují skutečné chování a data.

Projekty a sestavy

Každý projekt se zkompiluje do jednoho sestavení: knihovny tříd nebo spustitelného souboru. Začněte jedním projektem pro malé aplikace – nerozdělujte se předčasně. Primárním důvodem vytvoření samostatného projektu je opakované použití tohoto kódu ve více než jedné aplikaci. Kromě opakovaného použití přidejte projekty, pokud máte konkrétní důvod:

  • Sdílení kódu mezi aplikacemi – extrakce sdílené logiky do knihovny tříd, na kterou odkazuje více aplikací
  • Oddělte zájmy – mějte nezávislé vrstvy přístupu k datům, obchodní logiky a prezentace.
  • Řídit závislosti – projekt může používat pouze typy z projektů, na které explicitně odkazuje.

Jeden projekt funguje dobře pro mnoho aplikací. Odolávejte nutkání vytvářet separátní projekty "pro jistotu". Knihovnu můžete později kdykoli vytáhnout, pokud ji druhá aplikace bude potřebovat.

Přiřaďte jmenné prostory ke struktuře složek

Názvy jmenových prostor by měly odpovídat uspořádání složek projektu. Když vidíte obor názvů MyApp.Services.Payments, víte, že se máte podívat ve složce Services/Payments na zdrojový kód typů definovaných v tomto oboru názvů. Sada .NET SDK tuto konvenci podporuje a je tak široce následována, že její porušení aktivně mate ostatní vývojáře:

// File: Services/OrderService.cs
// Namespace mirrors the folder path
using MyApp.Core;

namespace MyApp.Services;

public class OrderService
{
    public Order CreateOrder(string product, int quantity, decimal price) =>
        new() { ProductName = product, Quantity = quantity, UnitPrice = price };

    public string FormatSummary(Order order) =>
        $"{order.Quantity}x {order.ProductName} = {order.Total:C}";
}

Kořenový obor názvů se automaticky nastaví podle názvu souboru projektu. Typy v podsložkách automaticky nezískají dílčí obory názvů – v každém souboru deklarujete obor názvů explicitně, ale vždy je budete synchronizovat.

Návod

Kořenový obor názvů můžete změnit nastavením <RootNamespace> v souboru projektu.

<Project Sdk="Microsoft.NET.Sdk">
 <PropertyGroup>
   <RootNamespace>MyCompany.MyApp</RootNamespace>
 </PropertyGroup>
</Project>

Uspořádejte jmenné prostory podle funkcí, nikoli podle typu.

Seskupte související typy do oborů názvů podle funkce nebo odpovědnosti. Umístěte rozhraní, jeho implementace a podpůrné typy dohromady:

// Good: group by feature
namespace MyApp.Payments;

public interface IPaymentProcessor
{
    bool ProcessPayment(decimal amount);
}

public class CreditCardProcessor : IPaymentProcessor
{
    public bool ProcessPayment(decimal amount)
    {
        Console.WriteLine($"Processing credit card payment of {amount:C}");
        return true;
    }
}

public record PaymentResult(bool Success, string? TransactionId);

Organizace založená na funkcích udržuje vše, co potřebujete, na jednom místě a usnadňuje navigaci v kódu a pochopení jeho struktury.

Modifikátory přístupu a sestavení

Modifikátory přístupu pracují se strukturou projektu a sestavení pro řízení přístupnosti:

Výchozí hodnota internal je pro typy, které ostatní projekty nepotřebují. Tento postup skryje podrobnosti implementace a dává vám svobodu k refaktorování bez přerušení uživatelů. Je to zvlášť důležité pro sdílené knihovny:

namespace MyApp.Inventory;

// Public — other projects can use this type
public class InventoryService
{
    public int GetStockLevel(string productName) =>
        StockDatabase.Lookup(productName);
}

// Internal — only visible within this assembly
internal static class StockDatabase
{
    private static readonly Dictionary<string, int> _stock = new()
    {
        ["Widget"] = 42,
        ["Gadget"] = 17
    };

    internal static int Lookup(string productName) =>
        _stock.GetValueOrDefault(productName);
}
  • Pojmenovávejte jmenné prostory konzistentně. Použijte CompanyName.ProductName.Feature jako vzor pojmenování. Například použijte Contoso.Inventory.Shipping. Konzistentní pojmenování pomáhá vývojářům najít typy bez hledání.
  • Zaměřte se na projekty. Každý projekt by měl mít jednu jasnou odpovědnost. Pokud projekt zpracovává příliš mnoho nesouvisejících problémů, rozdělte ho.
  • Použijte obory názvů omezené na soubor. Syntaxe namespace MyApp.Services; zmenšuje odsazení a je doporučeným stylem. Použijte ho ve všech nových kódech.
  • Výchozí je internal. Označujte typy public pouze v případě, že je ostatní sestavení skutečně potřebují. Přístup můžete kdykoli později rozšířit; jeho zúžení však představuje zásadní změnu.