Behavioral — Davranışsal Desenler
Interpreter
Yorumlayıcı Deseni
Amaç: Basit bir dilin gramerini sınıflarla temsil etmek ve bu dilde yazılmış ifadeleri yorumlayıp değerlendirmek.
Basit Anlatım
Teknik terim yok — günlük hayattan bir benzetme
Bir müzisyen nota kâğıdına bakıp şarkıyı çalar. Notalar aslında küçük bir dildir: her sembolün bir anlamı var (do, re, mi, uzun, kısa, sus). Müzisyen her sembolü tanır ve bir araya geldiklerinde ne anlama geldiğini "yorumlar".
Tarif kartı
Bir yemek tarifinde "2 su bardağı un + 1 yumurta, sonra 10 dakika karıştır" yazar. Bu da küçük bir dildir. Her ifade daha küçük ifadelerden oluşur: "2 su bardağı un" bir malzeme, "+" birleştirme, "10 dakika karıştır" bir işlem.
Yorumlayıcı, cümleyi parçalarına ayırır ve her parçayı kendi kuralına göre değerlendirir. Büyük ifadenin anlamı, küçük ifadelerin anlamlarının birleşimidir.
Teknik Anlamı
Bir yazılımcının bakış açısıyla
Interpreter, bir dilin gramerindeki her kuralı bir sınıf olarak temsil eder. İfadeler bu sınıflardan oluşan bir soyut sözdizimi ağacı (AST) olarak kurulur ve Interpret(context) metodu ağaç üzerinde özyinelemeli olarak çalışır. Yapısal olarak Composite deseninin özel bir uygulamasıdır.
Katılımcılar
- AbstractExpression:
Interpret(Context)metodunu bildirir. - TerminalExpression: Gramerin yaprakları; başka ifade içermez (sayılar, değişkenler).
- NonterminalExpression: Diğer ifadeleri içeren kurallar (toplama, çarpma, VE / VEYA).
- Context: Yorumlama sırasında gereken global bilgi (değişken değerleri vb.).
Kullanım alanları
- Kural motorları (iş kuralları: "yaş > 18 VE şehir = İstanbul")
- Basit hesap makineleri, formül değerlendiriciler
- Arama/filtre dilleri, SQL benzeri sorgu ifadeleri
- .NET'teki
System.Linq.Expressionsağaçları ve Regex motorları bu fikre dayanır.
Avantajlar
- Gramer kolayca genişletilebilir: yeni kural = yeni sınıf.
- Her kural kendi sınıfında olduğu için anlaşılır ve test edilebilirdir.
Dezavantajlar
- Karmaşık gramerler için sınıf sayısı patlar ve performans düşer. Bu durumda parser üreteçleri (ANTLR vb.) daha uygundur.
C# ile Uygulama
Adım adım, çalışan kod örneği
Senaryo: Bir indirim kural motoru. Kurallar şu şekilde tanımlanabilsin: "Toplam > 500 VE (Üye MI VEYA Kupon VAR MI)".
Adım 1 Context ve ifade arayüzü
public class Context
{
private readonly Dictionary<string, object> _values = new();
public void Set(string name, object value) => _values[name] = value;
public object Get(string name) => _values[name];
}
public interface IExpression
{
bool Interpret(Context ctx);
string Describe();
}Adım 2 Terminal ifadeler: gramerin yaprakları
public class IsTrue : IExpression
{
private readonly string _name;
public IsTrue(string name) => _name = name;
public bool Interpret(Context ctx) => (bool)ctx.Get(_name);
public string Describe() => _name;
}
public class GreaterThan : IExpression
{
private readonly string _name;
private readonly decimal _limit;
public GreaterThan(string name, decimal limit) { _name = name; _limit = limit; }
public bool Interpret(Context ctx) => (decimal)ctx.Get(_name) > _limit;
public string Describe() => $"{_name} > {_limit}";
}Adım 3 Nonterminal ifadeler: diğer ifadeleri birleştiren kurallar
Her biri alt ifadelerini yorumlatır ve sonucu birleştirir.
public class And : IExpression
{
private readonly IExpression _left, _right;
public And(IExpression left, IExpression right) { _left = left; _right = right; }
public bool Interpret(Context ctx) => _left.Interpret(ctx) && _right.Interpret(ctx);
public string Describe() => $"({_left.Describe()} VE {_right.Describe()})";
}
public class Or : IExpression
{
private readonly IExpression _left, _right;
public Or(IExpression left, IExpression right) { _left = left; _right = right; }
public bool Interpret(Context ctx) => _left.Interpret(ctx) || _right.Interpret(ctx);
public string Describe() => $"({_left.Describe()} VEYA {_right.Describe()})";
}
public class Not : IExpression
{
private readonly IExpression _inner;
public Not(IExpression inner) => _inner = inner;
public bool Interpret(Context ctx) => !_inner.Interpret(ctx);
public string Describe() => $"DEĞİL {_inner.Describe()}";
}Adım 4 Kullanım
Tüm parçaları bir araya getiren istemci (client) kodu:
// Kural: Toplam > 500 VE (Üye VEYA Kupon) VE DEĞİL Kara listede
IExpression rule =
new And(
new And(new GreaterThan("Toplam", 500),
new Or(new IsTrue("Üye"), new IsTrue("Kupon"))),
new Not(new IsTrue("KaraListe")));
Console.WriteLine($"Kural: {rule.Describe()}\n");
var customers = new (string Name, decimal Total, bool Member, bool Coupon, bool Banned)[]
{
("Ahmet", 750, true, false, false),
("Zeynep", 300, true, true, false),
("Burak", 900, false, true, false),
("Deniz", 1200, true, false, true),
};
foreach (var c in customers)
{
var ctx = new Context();
ctx.Set("Toplam", c.Total);
ctx.Set("Üye", c.Member);
ctx.Set("Kupon", c.Coupon);
ctx.Set("KaraListe", c.Banned);
Console.WriteLine($"{c.Name,-7} -> indirim {(rule.Interpret(ctx) ? "UYGULANIR" : "yok")}");
}Konsol çıktısı
Kural: ((Toplam > 500 VE (Üye VEYA Kupon)) VE DEĞİL KaraListe) Ahmet -> indirim UYGULANIR Zeynep -> indirim yok Burak -> indirim UYGULANIR Deniz -> indirim yok
Kodda dikkat edilmesi gerekenler
- Kural, küçük yapı taşlarının birleşiminden oluşan bir ağaç. Aynı ağaç farklı müşteri verileriyle (context) defalarca değerlendirildi.
- "Küçüktür", "Eşittir" gibi yeni kurallar eklemek için yeni bir
IExpressionsınıfı yazmak yeterli. - Gerçek bir uygulamada bu ağaç, kullanıcının yazdığı bir metinden (parser ile) veya veritabanındaki kural tanımlarından otomatik oluşturulabilir.