C# Specification Pattern ile Esnek ve Okunabilir Kod Yazma

Yazılım geliştirme sürecinde, karmaşık iş kurallarını yönetmek sıklıkla önemli bir gerekliliktir. Bu iş kuralları genellikle nesneler arasındaki ilişkileri, durumları ve davranışları ifade eder. Ancak, bu iş kurallarını kodlamak ve sürdürmek karmaşıklaşabilir ve zamanla daha zor hale gelebilir. İşte tam da burada Specification Pattern (Belirleme Deseni) devreye girer.

Specification Pattern Nedir?

Specification Pattern, bir nesnenin belirli bir kriter kümesini karşılayıp karşılamadığını değerlendirmek için kullanılan bir tasarım desenidir. Bu desen, iş kurallarını tanımlamak ve kodlamak için daha yapılandırılmış bir yaklaşım sunar. Ayrıca, iş kurallarını birbirinden bağımsız olarak kapsüller, okunabilirlik ve sürdürülebilirlik sağlar.

Specification Pattern, birkaç temel bileşenden oluşur:

  • Specification (Belirleme): Bir iş kuralını temsil eden soyut bir sınıftır. Belirli bir nesnenin bu belirlemeyi karşılayıp karşılamadığını kontrol etmek için "IsSatisfiedBy" adında bir metod içerir.

  • ConcreteSpecification (Somut Belirleme): Soyut belirleme sınıfından türetilen ve belirli bir iş kuralını uygulayan somut bir sınıftır. "IsSatisfiedBy" metodunu uygulayarak belirli bir nesnenin kriterleri karşılayıp karşılamadığını kontrol eder.

  • Entity (Varlık): İş kurallarının uygulanacağı nesneleri temsil eden sınıflardır.

  • CompositeSpecification (Bileşik Belirleme): Birden çok belirlemeyi birleştiren ve mantıksal operatörlerle birleştirme işlemlerini gerçekleştiren soyut bir sınıftır. Bu sınıf, birden fazla belirlemeyi birleştirerek daha karmaşık iş kurallarını ifade etmek için kullanılır.

Örnek Senaryo: Film Kütüphanesi

Film kütüphanesi uygulaması yazarken Specification Pattern'ı kullanarak iş kurallarını uygulamayı ele alalım. Bu uygulama, kullanıcılara film kataloğunu arama ve filtreleme yeteneği sunacak.

İlk adımda, Specification (Belirleme) ve ConcreteSpecification (Somut Belirleme) sınıflarını tanımlayalım:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public abstract class Specification<T>
{
    public abstract bool IsSatisfiedBy(T entity);
 
    public Specification<T> And(Specification<T> other)
    {
        return new AndSpecification<T>(this, other);
    }
 
    public Specification<T> Or(Specification<T> other)
    {
        return new OrSpecification<T>(this, other);
    }
 
    public Specification<T> Not()
    {
        return new NotSpecification<T>(this);
    }
}
 
public class AndSpecification<T> : Specification<T>
{
    private Specification<T> _left;
    private Specification<T> _right;
 
    public AndSpecification(Specification<T> left, Specification<T> right)
    {
        _left = left;
        _right = right;
    }
 
    public override bool IsSatisfiedBy(T entity)
    {
        return _left.IsSatisfiedBy(entity) && _right.IsSatisfiedBy(entity);
    }
}
 
public class OrSpecification<T> : Specification<T>
{
    private Specification<T> _left;
    private Specification<T> _right;
 
    public OrSpecification(Specification<T> left, Specification<T> right)
    {
        _left = left;
        _right = right;
    }
 
    public override bool IsSatisfiedBy(T entity)
    {
        return _left.IsSatisfiedBy(entity) || _right.IsSatisfiedBy(entity);
    }
}
 
public class NotSpecification<T> : Specification<T>
{
    private Specification<T> _specification;
 
    public NotSpecification(Specification<T> specification)
    {
        _specification = specification;
    }
 
    public override bool IsSatisfiedBy(T entity)
    {
        return !_specification.IsSatisfiedBy(entity);
    }
}

Şimdi, iş kurallarını uygulamak için ConcreteSpecification (Somut Belirleme) sınıflarını tanımlayalım:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class Film
{
    public string Ad { get; set; }
    public int YapimYili { get; set; }
    public string Tur { get; set; }
    public double IMDBPuani { get; set; }
}
 
public class YapimYiliBelirleme : Specification<Film>
{
    private readonly int _yil;
 
    public YapimYiliBelirleme(int yil)
    {
        _yil = yil;
    }
 
    public override bool IsSatisfiedBy(Film film)
    {
        return film.YapimYili == _yil;
    }
}
 
public class TurBelirleme : Specification<Film>
{
    private readonly string _tur;
 
    public TurBelirleme(string tur)
    {
        _tur = tur;
    }
 
    public override bool IsSatisfiedBy(Film film)
    {
        return film.Tur.Equals(_tur, StringComparison.OrdinalIgnoreCase);
    }
}
 
public class IMDBPuaniBelirleme : Specification<Film>
{
    private readonly double _puan;
 
    public IMDBPuaniBelirleme(double puan)
    {
        _puan = puan;
    }
 
    public override bool IsSatisfiedBy(Film film)
    {
        return film.IMDBPuani >= _puan;
    }
}
Son olarak, Specification Pattern'ını kullanarak filtrelemeyi gerçekleştirelim:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class FilmKutuphanesi
{
    private readonly List<Film> _filmler = new List<Film>();
 
    public void FilmiEkle(Film film)
    {
        _filmler.Add(film);
    }
 
    public List<Film> FilmleriFiltrele(Specification<Film> spec)
    {
        return _filmler.Where(film => spec.IsSatisfiedBy(film)).ToList();
    }
}
Bu örnekte, FilmKutuphanesi sınıfı belirli bir film koleksiyonunu temsil eder ve FilmleriFiltrele metodunu kullanarak belirli bir Specification (Belirleme) nesnesine uyan filmleri filtreler.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var filmKutuphanesi = new FilmKutuphanesi();
 
// Filmleri oluştur
var film1 = new Film { Ad = "Interstellar", YapimYili = 2014, Tur = "Bilim Kurgu", IMDBPuani = 8.6 };
var film2 = new Film { Ad = "The Dark Knight", YapimYili = 2008, Tur = "Aksiyon", IMDBPuani = 9.0 };
var film3 = new Film { Ad = "Inception", YapimYili = 2010, Tur = "Bilim Kurgu", IMDBPuani = 8.8 };
 
filmKutuphanesi.FilmiEkle(film1);
filmKutuphanesi.FilmiEkle(film2);
filmKutuphanesi.FilmiEkle(film3);
 
// Filmleri filtrele
var bilimKurguBelirleme = new TurBelirleme("Bilim Kurgu");
var yilBelirleme = new YapimYiliBelirleme(2010);
var puanBelirleme = new IMDBPuaniBelirleme(8.8);
 
var filtrelenenFilmler = filmKutuphanesi.FilmleriFiltrele(bilimKurguBelirleme.And(yilBelirleme).And(puanBelirleme));
 
foreach (var film in filtrelenenFilmler)
{
    Console.WriteLine(film.Ad);
}

Bu örnekte, filmleri "Bilim Kurgu" türünde, 2010 yapımı ve 8.8 ve üzeri IMDB puanına sahip olanları filtreledik.

Specification Pattern, iş kurallarını daha anlaşılır ve sürdürülebilir bir şekilde ifade etmemizi sağlar. Ayrıca, filtreleme mantığını birbirinden bağımsız ve yeniden kullanılabilir parçalara ayırarak kodun okunabilirliğini artırır. Bu sayede, gelecekteki değişikliklerde kodu daha kolay güncelleyebiliriz.

Bu makalede, C# dilinde Specification Pattern'ın temellerini ve nasıl kullanıldığını örneklerle anlattık. Bu deseni daha karmaşık iş kurallarını yönetmek için kullanabilir ve yazılımınızı daha esnek hale getirebilirsiniz.

Yorumlar kapalı