Giriş

Kod yazarken bazen fonksiyonlarımızın başı, uzun ve iç içe geçmiş if bloklarıyla dolu hale gelir. Özellikle hata kontrolleri veya parametre doğrulamaları söz konusu olduğunda kod okunabilirliğini kaybeder.
İşte tam bu noktada Guard Clause Pattern, kodunuzu sadeleştirip “happy path”i daha net hale getirmenizi sağlar.

guard_clause_pattern

Bu yazıda, C# özelinde Guard Clause Pattern nedir, neden kullanılır ve nasıl uygulanır adım adım inceleyeceğiz.

Guard Clause Pattern Nedir?

Guard Clause, bir metodun başında geçersiz veya istenmeyen durumları erken tespit edip fonksiyondan çıkmayı sağlayan bir yaklaşımdır. Bu sayede, gereksiz else bloklarından kurtulur, kodun ana amacını (happy path) daha net bir şekilde ortaya çıkarırsınız.

Klasik Yaklaşım (Karmaşık Kod)

Aşağıdaki örneğe bakalım:

public IActionResult GetCategory(int id)
{
    if (id <= 0)
    {
        return BadRequest("Geçersiz kategori ID");
    }
    else
    {
        var category = _repository.GetById(id);
        if (category == null)
        {
            return NotFound("Kategori bulunamadı");
        }
        else
        {
            return Ok(category);
        }
    }
}

Bu kod çalışır, ancak iç içe geçmiş if blokları nedeniyle okunabilirlik düşüktür.

Guard Clause ile Basitleştirilmiş Yaklaşım

Aynı kodu Guard Clause Pattern kullanarak şöyle yazabiliriz:

public IActionResult GetCategory(int id)
{
    if (id <= 0)
        return BadRequest("Geçersiz kategori ID");

    var category = _repository.GetById(id);
    if (category == null)
        return NotFound("Kategori bulunamadı");

    return Ok(category);
}

Gördüğünüz gibi, “hatalı durumları” başta kontrol edip metottan erken çıkıyoruz. Artık fonksiyonun ana akışı (kategori döndürme kısmı) daha net ve sade görünüyor.

Gerçek Hayattan Bir Örnek: MediatR ile Guard Clause Kullanımı

Aşağıdaki örnek, bir MediatR handler içinde Guard Clause kullanımını gösteriyor:

public class GetCategoryQueryHandler : IRequestHandler<GetCategoryQuery, TResult<List<GetCategoryQueryResult>>>
{
    private readonly IRepository<Category> _repository;
    private readonly IMapper _mapper;

    public GetCategoryQueryHandler(IRepository<Category> repository, IMapper mapper)
    {
        _repository = repository;
        _mapper = mapper;
    }

    public async Task<TResult<List<GetCategoryQueryResult>>> Handle(GetCategoryQuery request, CancellationToken cancellationToken)
    {
        var categories = await _repository.GetAllAsync();
        if (categories == null || categories.Count == 0)
        {
            return TResult<List<GetCategoryQueryResult>>.Failure(ErrorType.NotFound, "Category not found");
        }

        var result = _mapper.Map<List<GetCategoryQueryResult>>(categories);

        return TResult<List<GetCategoryQueryResult>>.Success(result);
    }
}
public class CreateCategoryCommandHandler : IRequestHandler<CreateCategoryCommand, TResult<Guid>>
{
    private readonly IRepository<Category> _repository;
    private readonly IUnitOfWork _unitOfWork;
    private readonly IMapper _mapper;

    public CreateCategoryCommandHandler(IRepository<Category> repository, IUnitOfWork unitOfWork, IMapper mapper)
    {
        _repository = repository;
        _unitOfWork = unitOfWork;
        _mapper = mapper;
    }

    public async Task<TResult<Guid>> Handle(CreateCategoryCommand request, CancellationToken ct)
    {
        var exists = await _repository.GetSingleAsync(x => x.Name == request.Name);
        if (exists is not null)
            return TResult<Guid>.Failure(ErrorType.Conflict, "Category name already exists");

        var category = _mapper.Map<Category>(request);
        await _repository.AddAsync(category);

        try
        {
            var affected = await _unitOfWork.SaveChangesAsync();
            return TResult<Guid>.Success(category.Id);
        }
        catch (Exception ex)
        {
            return TResult<Guid>.Exception(ex.Message);
        }
    }
}

Bu örnekte, önce hatalı durum kontrol ediliyor. Eğer hata varsa erken dönülüyor (return), aksi halde işlem başarıyla devam ediyor. Bu yapı, guard clause pattern’in en sade halidir.

Guard Clause Kullanmanın Faydaları

  1. Okunabilirlik Artar: Kodun ana amacı daha belirgin olur.
  2. Bakımı Kolaylaşır: Gereksiz iç içe if blokları ortadan kalkar.
  3. Test Edilebilirlik İyileşir: Hatalı durumlar kolayca izole edilip test edilebilir.
  4. Temiz Kod İlkelerine Uygundur: Özellikle “Single Responsibility” ve “Early Return” prensiplerini destekler.

Ne Zaman Kullanılmalı?

Guard Clause Pattern özellikle şu durumlarda faydalıdır:

  • Metot başında parametre veya veri doğrulaması yapılacaksa
  • Hatalı senaryoları erken tespit edip akışı durdurmak gerekiyorsa
  • “Happy path”’in net ve sade görünmesi isteniyorsa

Sonuç

Guard Clause Pattern, küçük bir değişiklikle kod kalitesinde büyük fark yaratabilir. C#’ta bu yaklaşımı benimseyerek, hem kendiniz hem de ekibiniz için daha temiz, daha okunabilir ve sürdürülebilir kod yazabilirsiniz.

By tanju.bozok

Software Architect, Developer, and Entrepreneur

One thought on “C#’ta Guard Clause Pattern Nedir? Kod Kalitesini Artıran Basit Bir Yaklaşım”
  1. .NET hakkında yazdığınız bloglar benim için çok kıymetli. Emeğinize sağlık.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir