﻿
{"id":25885,"date":"2022-11-28T04:39:54","date_gmt":"2022-11-28T04:39:54","guid":{"rendered":"https:\/\/www.gencayyildiz.com\/blog\/?p=25885"},"modified":"2022-11-28T04:39:54","modified_gmt":"2022-11-28T04:39:54","slug":"c-ile-specification-patterni-inceleyelim","status":"publish","type":"post","link":"https:\/\/www.gencayyildiz.com\/blog\/c-ile-specification-patterni-inceleyelim\/","title":{"rendered":"C# \u0130le Specification Pattern&#8217;\u0131 \u0130nceleyelim"},"content":{"rendered":"<div id=\"fb-root\"><\/div>\n<p>Merhaba,<\/p>\n<p>Bu i\u00e7eri\u011fimizde, domain i\u00e7erisinde i\u015f kurallar\u0131n\u0131 kaps\u00fclleyerek kod taban\u0131nda farkl\u0131 noktalarda yeniden kullan\u0131labilir k\u0131lmam\u0131z\u0131 ve b\u00f6ylece bu kurallar\u0131 tek bir yerden merkezi olarak y\u00f6netmemizi sa\u011flayan <em>Specification<\/em> modelini ele alacak ve detayl\u0131 bir \u015fekilde nas\u0131l uyguland\u0131\u011f\u0131n\u0131 irdeliyor olaca\u011f\u0131z.<\/p>\n<h4>Specification Pattern Nedir?<\/h4>\n<p>Specification Pattern, genellikle <a href=\"https:\/\/www.gencayyildiz.com\/blog\/domain-driven-design-stratejik-ve-taktiksel-olarak-derinlemesine-inceleme\/\" rel=\"noopener\" target=\"_blank\">Domain Driven Design (DDD)<\/a>&#8216;da i\u015f kurallar\u0131n\u0131 ba\u011f\u0131ms\u0131z mant\u0131ksal birimler halinde merkezi bir noktada \u00f6zetleyerek, kodun yeniden kullan\u0131labilir olmas\u0131n\u0131 ama\u00e7layan bir yakla\u015f\u0131md\u0131r.<\/p>\n<p>Burada <u>i\u015f kurallar\u0131<\/u>ndan kastedileni net bir \u015fekilde izah edebilmek i\u00e7in \u015f\u00f6yle bir metafor \u00fczerinden misalde bulunmam\u0131z yerinde olacakt\u0131r; Diyelim ki uygulamam\u0131z ile ilgili belirli gereksinimlerimiz s\u00f6z konusu ve bu gereksinimlerden sadece birinin a\u015fa\u011f\u0131daki gibi oldu\u011funu varsayal\u0131m.<\/p>\n<blockquote><p><em>uygulaman\u0131z\u0131n kullan\u0131c\u0131 hedef kitlesi 18 ya\u015f \u00fczerinde olsun&#8230;<\/em><\/p><\/blockquote>\n<p>Bu gereksinimi kar\u015f\u0131layabilmek i\u00e7in a\u015fa\u011f\u0131daki gibi bir davran\u0131\u015f sergileyebilirsiniz.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nvoid Register(UserInfo userInfo)\r\n{\r\n    if (userInfo.Age &lt; 18)\r\n        throw new Exception(&quot;Too young...&quot;);\r\n}\r\n<\/pre>\n<\/div>\n<p>\u00d6yle de\u011fil mi? \u0130\u015fte burada yazm\u0131\u015f oldu\u011fumuz if kal\u0131b\u0131 bir i\u015f kural\u0131n\u0131n somutla\u015fm\u0131\u015f \u00f6rne\u011fidir. Bunun basit bir tan\u0131mlama oldu\u011funun fark\u0131nday\u0131m. Burada m\u00fchim olan nokta \u015fudur ki, bir i\u015f kural\u0131 kod i\u00e7erisinde geli\u015ftirici a\u00e7\u0131s\u0131ndan yapt\u0131\u011f\u0131 fiiliyat\u0131n d\u0131\u015f\u0131nda bir anlam ifade etmeyebilir. T\u0131pk\u0131 burada oldu\u011fu gibi. Yaz\u0131lan bu kodu herhangi bir geli\u015ftirici inceledi\u011finde &#8216;Age&#8217; de\u011feri 18&#8217;den k\u00fc\u00e7\u00fck oldu\u011fu durumda bir exception f\u0131rlat\u0131laca\u011f\u0131n\u0131 bilmekte ama bu bilginin uygulaman\u0131n geneline dair kabul edilmi\u015f bir davran\u0131\u015f m\u0131 yoksa sadece burada uygulanan fiili bir hamle mi olup olmad\u0131\u011f\u0131n\u0131 bilememektedir. Ya da bu i\u015f kural\u0131ndan yola \u00e7\u0131k\u0131p bu uygulamay\u0131 18 ya\u015f\u0131ndan k\u00fc\u00e7\u00fcklerin kullan\u0131p kullanamayaca\u011f\u0131na dair bir fikir de edinemeyecektir. Yani anlayaca\u011f\u0131n\u0131z i\u015f kurallar\u0131n\u0131n esas mahiyetleri a\u00e7\u0131s\u0131ndan anlam kazanabilmeleri i\u00e7in ya analiz ekiplerindeki personeller taraf\u0131ndan izah edilmeleri ya da dok\u00fcmante edilmeleri gerekmektedir.<\/p>\n<p>\u015eimdi gelin bu \u00f6rnek \u00fczerinden konumuz olan Specification Pattern&#8217;\u0131 temelde \u00f6rneklendirerek hafiften i\u00e7eri\u011fe de girizgah eyleyelim&#8230;<\/p>\n<p>Bizler bu i\u015f kural\u0131n\u0131 if kal\u0131b\u0131ndan ziyade bir kod konseptine d\u00f6n\u00fc\u015ft\u00fcr\u00fcrsek e\u011fer a\u015fa\u011f\u0131daki gibi bir kullan\u0131m s\u00f6z konusu olacakt\u0131r.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nvoid Register(UserInfo userInfo)\r\n{\r\n    UserSpecification specification = new();\r\n    if (specification.IsSatisfiedBy(userInfo))\r\n        throw new Exception(&quot;Too young...&quot;);\r\n}\r\n\r\nclass UserSpecification\r\n{\r\n    private int _age;\r\n\r\n    public UserSpecification(int age = 18)\r\n        =&gt; _age = age;\r\n\r\n    public bool IsSatisfiedBy(UserInfo userInfo)\r\n        =&gt; userInfo.Age &gt; _age;\r\n}\r\n<\/pre>\n<\/div>\n<p>Ve bu kullan\u0131mda bir i\u015f kural\u0131 uygulamaktan ziyade <code>IsSatisfiedBy<\/code> metodu ile bir do\u011frulaman\u0131n yapt\u0131r\u0131ld\u0131\u011f\u0131 g\u00f6r\u00fcld\u00fc\u011f\u00fc i\u00e7in art\u0131k di\u011fer geli\u015ftiriciler a\u00e7\u0131s\u0131ndan buradaki yap\u0131lan i\u015flem salt bir fiiliyata nazaran daha fazla bir anlam ifade edebilecektir. \u00c7\u00fcnk\u00fc art\u0131k bu metot bir i\u015f kural\u0131n\u0131n d\u0131\u015f\u0131nda, o kural\u0131 uygulayan bir \u015fartname(specification) g\u00f6revi g\u00f6rmektedir. Bu \u015fartnamenin kullan\u0131ld\u0131\u011f\u0131 her noktada i\u00e7erisindeki \u015fart\/kural uygulama s\u00fcrecinde icra edilecek ve i\u015fin g\u00fczel yan\u0131 bu yap\u0131 sayesinde bu i\u015f mant\u0131\u011f\u0131 merkezi bir noktadan y\u00f6netilebilir bir nitelik kazanm\u0131\u015f olacakt\u0131r. Yani bir g\u00fcn bu i\u015f kural\u0131n\u0131 de\u011fi\u015ftirme karar\u0131 verildi\u011fi taktirde uyguland\u0131\u011f\u0131 her noktada hususi de\u011fi\u015fiklik yapmak yerine bu \u015fartname \u00fczerinden tek seferlik de\u011fi\u015fiklikle yeni davran\u0131\u015f t\u00fcm noktalara merkezi olarak yans\u0131t\u0131lm\u0131\u015f olunacakt\u0131r.<\/p>\n<p>\u0130\u015fte Specification Pattern&#8217;\u0131n gayesi bu mant\u0131kta i\u015f kurallar\u0131n\u0131 temellendirmek ve merkezi hale getirmek \u00fczerine kuruludur&#8230;<\/p>\n<h4>Specification Pattern&#8217;\u0131n \u0130htiya\u00e7 Duyuldu\u011fu Noktalar\u0131 \u0130nceleyelim<\/h4>\n<p>Specification pattern&#8217;\u0131n ihtiyac\u0131n\u0131 daha net g\u00f6sterebilmek i\u00e7in a\u015fa\u011f\u0131daki s\u0131n\u0131f \u00fczerinden de\u011ferlendirmede bulunabiliriz;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nabstract class Entity\r\n{\r\n    public int Id { get; set; }\r\n}\r\n\r\nclass Person : Entity\r\n{\r\n    public string Name { get; set; }\r\n    public int Age { get; set; }\r\n    public Gender Gender { get; set; }\r\n    public DateTime CreatedDate { get; set; }\r\n}\r\n\r\nenum Gender\r\n{\r\n    Woman,\r\n    Man\r\n}\r\n<\/pre>\n<\/div>\n<p>\u015eimdi diyelim ki, yeni kay\u0131t olan personelleri elde etmek istiyoruz. Bunun i\u00e7in bir repository s\u0131n\u0131f\u0131nda a\u015fa\u011f\u0131daki gibi bir \u00e7al\u0131\u015fma ger\u00e7ekle\u015ftirilebilir.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nclass PersonRepository\r\n{\r\n    public IReadOnlyList&lt;Person&gt; GetByCreatedDate(DateTime minCreatedDate)\r\n    {\r\n        \/\/...\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<p>Yahut cinsiyete, ya\u015fa vs. gibi aramalar yapmak istersek de a\u015fa\u011f\u0131daki gibi metotlar e\u015fli\u011finde \u00e7al\u0131\u015fmam\u0131z\u0131 ger\u00e7ekle\u015ftirebiliriz.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nclass PersonRepository\r\n{\r\n    public IReadOnlyList&lt;Person&gt; GetByCreatedDate(DateTime minCreatedDate)\r\n    {\r\n        \/\/...\r\n    }\r\n    public IReadOnlyList&lt;Person&gt; GetByAge(int age)\r\n    {\r\n        \/\/...\r\n    }\r\n    public IReadOnlyList&lt;Person&gt; GetByGender(Gender gender)\r\n    {\r\n        \/\/...\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<p>Arama kriterleri bu \u015fekilde tekil olarak de\u011ferlendirildi\u011finde rahatl\u0131kla ayr\u0131 metotlar \u00fczerinde bu i\u015flemleri ger\u00e7ekle\u015ftirebilmekteyiz. Amma velakin, bu kriterleri birle\u015ftirmeye karar verdi\u011fimizde i\u015flerimiz biraz daha karma\u015f\u0131kla\u015facakt\u0131r.<\/p>\n<p>Tabi, t\u00fcm arama kriterlerini k\u00fcm\u00fclatif olarak bar\u0131nd\u0131ran a\u015fa\u011f\u0131daki gibi bir metot geli\u015ftirerek de bu ihtiyac\u0131 rahatl\u0131kla kar\u015f\u0131layabiliriz.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nclass PersonRepository\r\n{\r\n    public IReadOnlyList&lt;Person&gt; Find(DateTime? minCreatedDate = null, int? age, Gender? gender = null)\r\n    {\r\n        \/\/...\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<p>Elbet bu metoda da daha fazla kriter ekleyerek geni\u015fletebiliriz. Evet, kriterleri b\u00fct\u00fcnle\u015fik olarak bu metotla kar\u015f\u0131layarak ihtiya\u00e7lar\u0131m\u0131z\u0131 b\u00f6ylece giderebiliriz ama bu y\u00f6ntemin sizlerin de i\u00e7ine sinmedi\u011fini biliyorum \ud83d\ude42<\/p>\n<p>\u0130\u00e7eri\u011fimizin devam\u0131nda buradaki ihtiya\u00e7lar\u0131 daha ideal bir \u015fekilde kar\u015f\u0131layabilmemizi sa\u011flayacak olan Specification pattern yakla\u015f\u0131m\u0131yla ele al\u0131yor olaca\u011f\u0131z. \u015eimdilik \u00f6rnek senaryoyu biraz daha a\u00e7maya devam edelim.<\/p>\n<p>Bazen yap\u0131lan \u00e7al\u0131\u015fmalarda hedef verileri veritaban\u0131ndan \u00e7ektikten sonra da in-memory&#8217;de do\u011frulamalar ger\u00e7ekle\u015ftirmemiz gerekebilmektedir. \u00d6rne\u011fin, bir person&#8217;\u0131 herhangi bir g\u00f6reve tabi tutmadan \u00f6nce ya\u015f\u0131n\u0131n bu g\u00f6rev i\u00e7in uygun olup olmad\u0131\u011f\u0131n\u0131 do\u011frulamak isteriz.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nclass PersonService\r\n{\r\n    PersonRepository _personRepository = new();\r\n    public bool AssignToPerson(int personId)\r\n    {\r\n        Person person = _personRepository.GetById(personId);\r\n        if (person.Age &lt; 18)\r\n            throw new Exception(&quot;The person is not eligible for task.&quot;);\r\n\r\n        return true;\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<p>Benzer \u015fekilde ayn\u0131 kriteri kar\u015f\u0131layan di\u011fer person&#8217;lar\u0131 da veritaban\u0131ndan sorgulamakta isteyebiliriz. Bunun i\u00e7in de a\u015fa\u011f\u0131daki gibi repository s\u0131n\u0131f\u0131nda bir i\u015flem uygulayabiliriz.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nclass PersonRepository\r\n{\r\n    public IReadOnlyList&lt;Person&gt; FindPeopleFitForTheJob()\r\n    {\r\n        return dbContext.Where(person =&gt; person.Age &gt; 18).ToList();\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<p>\u015eimdi burada durup, yapm\u0131\u015f oldu\u011fumuz i\u015flemleri de\u011ferlendirirsek e\u011fer bir person ile ilgili i\u015f kurallar\u0131 ve domain bilgisi gere\u011fi farkl\u0131 davran\u0131\u015flar\u0131 farkl\u0131 s\u0131n\u0131flar ve metotlar \u00fczerinde tan\u0131mlamak zorunda kald\u0131\u011f\u0131m\u0131z\u0131 g\u00f6r\u00fcyorsunuz. Ve hatta bu s\u0131n\u0131flar da <em>Don\u2019t Repeat Yourself(DRY)<\/em> ilkesini ihlal ederek ister istemez tekrarl\u0131 i\u015flemler ger\u00e7ekle\u015ftirebiliyoruz. \u0130\u015fte Specification pattern&#8217;\u0131n bizlere yard\u0131mc\u0131 olabilece\u011fi yer tam da buras\u0131d\u0131r.<\/p>\n<blockquote><p><em style=\"color:green;\">Specification pattern, farkl\u0131 \u015fartlara ba\u011fl\u0131 olacak \u015fekilde de\u011ferlendirmelerde bulunabilecek ya da bir ba\u015fka deyi\u015fle de\u011ferlendirmeleri tam olarak nas\u0131l ay\u0131rt edebilece\u011fini bilecek s\u0131n\u0131flar tan\u0131mlamam\u0131z\u0131 ve domain bilgisi gere\u011fi t\u00fcrl\u00fc i\u015f kurallar\u0131n\u0131 bu s\u0131n\u0131flar \u00fczerinden uygulamam\u0131z\u0131 savunan bir tasar\u0131m desenidir.<\/em><\/p><\/blockquote>\n<p>Misal olarak a\u015fa\u011f\u0131daki gibi i\u015fe uygun olan person&#8217;lar\u0131 ifade eden bir \u015fartname olarak &#8216;PeopleFitForTheJobSpecification&#8217; s\u0131n\u0131f\u0131n\u0131 tan\u0131mlayabiliriz.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nclass PeopleFitForTheJobSpecification : Specification&lt;Person&gt;\r\n{\r\n    public override Expression&lt;Func&lt;Person, bool&gt;&gt; ToExpression()\r\n    {\r\n        return p =&gt; p.Age &gt; 18;\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<p>Biliyorum, base class olarak kullan\u0131lan <code>Specification&lt;T&gt;<\/code> s\u0131n\u0131f\u0131n\u0131n nereden geldi\u011fini merak ediyorsunuz. Zaten i\u015fin t\u00fcm detay\u0131 orada \ud83d\ude42 Onu ilerideki sat\u0131rlar\u0131m\u0131zda ayr\u0131ca konu\u015fuyor olaca\u011f\u0131z. \u015eimdilik bu specification ile farkl\u0131 s\u0131n\u0131flarda tekrar eden i\u015f kurallar\u0131n\u0131n nas\u0131l merkezi hale getirildi\u011fini g\u00f6rerek devam edelim.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nclass PersonService\r\n{\r\n    PersonRepository _personRepository = new();\r\n    public bool AssignToPerson(int personId)\r\n    {\r\n        Person person = _personRepository.GetById(personId);\r\n\r\n        PeopleFitForTheJobSpecification specification = new();\r\n\r\n        if (!specification.IsSatisfiedBy(person))\r\n            throw new Exception(&quot;The person is not eligible for task.&quot;);\r\n\r\n        return true;\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nclass PersonRepository\r\n{\r\n    public IReadOnlyList&lt;Person&gt; FindPeopleFitForTheJob()\r\n    {\r\n        PeopleFitForTheJobSpecification specification = new();\r\n        return dbContext.Where(person =&gt; specification.IsSatisfiedBy(person)).ToList();\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<p>Dikkat ederseniz bu yakla\u015f\u0131m yaln\u0131zca i\u015f kurallar\u0131n\u0131n ve domain bilgisi gere\u011fi farkl\u0131 davran\u0131\u015flar\u0131n tekrarlar\u0131n\u0131 ortadan kald\u0131rmakla kalmamakta, ayn\u0131 zamanda birden \u00e7ok \u00f6zelli\u011fin birle\u015ftirilmesine de olanak sa\u011flayabilecek bir altyap\u0131 sunmaktad\u0131r. B\u00f6ylece olduk\u00e7a karma\u015f\u0131k arama ve do\u011frulama kriterlerini kolayca olu\u015fturmam\u0131za yard\u0131mc\u0131 olmaktad\u0131r.<\/p>\n<p>Buradan yola \u00e7\u0131karak \u015f\u00f6yle bir genellemeye varabiliriz ki, Specification pattern iki ana noktada kullan\u0131ma sunulabilmektedir;<\/p>\n<ul>\n<li><em>Veritaban\u0131ndan elimizdeki specification&#8217;a uyan hedef verileri sorgularken.<\/em><\/li>\n<li><em>Veritaban\u0131ndan sorgu neticesinde gelen veya d\u0131\u015f bir servis taraf\u0131ndan g\u00f6nderilen yahut kendimizce create edilen herhangi bir nesnenin ya da nesnelerin i\u015f kurallar\u0131na uyup uymad\u0131\u011f\u0131n\u0131 in-memory&#8217;de kontrol ederken.<\/em><\/li>\n<\/ul>\n<h4>Specification Pattern Nas\u0131l Uygulan\u0131r?<\/h4>\n<p>Asl\u0131nda specification pattern&#8217;\u0131n i\u015f kurallar\u0131n\u0131 bar\u0131nd\u0131ran s\u0131n\u0131flar ile uygulanaca\u011f\u0131n\u0131 yukar\u0131daki sat\u0131rlar\u0131m\u0131zda yapt\u0131\u011f\u0131m\u0131z izahatlerden anlam\u0131\u015fs\u0131n\u0131zd\u0131r diye d\u00fc\u015f\u00fcn\u00fcyorum. Biz yine de olay\u0131 daha da derinle\u015ftirecek ve pratiksel olarak i\u015fin usul\u00fcn\u00fc tam teferruatl\u0131 masaya yat\u0131raca\u011f\u0131z. \u015eimdi \u00f6ncelikle i\u015f kurallar\u0131n\u0131 bar\u0131nd\u0131racak s\u0131n\u0131flar\u0131 olu\u015fturabilmek i\u00e7in &#8216;PeopleFitForTheJobSpecification&#8217; s\u0131n\u0131f\u0131nda kulland\u0131\u011f\u0131m\u0131z gibi generic olan base <code>Specification&lt;T&gt;<\/code> s\u0131n\u0131f\u0131n\u0131 olu\u015fturmam\u0131z gerekmektedir. <\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nabstract class Specification&lt;T&gt;\r\n{\r\n    public abstract Expression&lt;Func&lt;T, bool&gt;&gt; ToExpression();\r\n    public bool IsSatisfiedBy(T entity)\r\n    {\r\n        Func&lt;T, bool&gt; predicate = ToExpression().Compile();\r\n        return predicate(entity);\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<p>Bu s\u0131n\u0131f domain bilgisi gere\u011fi tan\u0131mlanacak i\u015f kural\u0131n\u0131n uyumlu olup olmad\u0131\u011f\u0131n\u0131 kontrol edebilmek i\u00e7in &#8216;IsSatisfiedBy&#8217; metodunu bar\u0131nd\u0131rmaktad\u0131r. Bu metot i\u00e7erisinde &#8216;ToExpression&#8217; isimli abstract olarak tan\u0131mlanm\u0131\u015f metot imzas\u0131 compile edilmekte ve gerekli kontrol bunun \u00fczerinden ger\u00e7ekle\u015ftirilerek uygulamadan encapsulate edilmektedir.<\/p>\n<p>Bu tan\u0131mlamadan sonra uygulama s\u00fcrecinde kullanaca\u011f\u0131m\u0131z t\u00fcm i\u015f kurallar\u0131n\u0131 yukar\u0131daki &#8216;PeopleFitForTheJobSpecification&#8217; s\u0131n\u0131f\u0131nda oldu\u011fu gibi \u00f6zel specification s\u0131n\u0131flar\u0131 ile temsil edebiliriz.<\/p>\n<p>Bir ka\u00e7 \u00f6rnek yaparsak e\u011fer;<\/p>\n<p>Sadece erkek ki\u015fileri temsil eden specification;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nclass OnlyMenSpecification : Specification&lt;Person&gt;\r\n{\r\n    public override Expression&lt;Func&lt;Person, bool&gt;&gt; ToExpression()\r\n    {\r\n        return p =&gt; p.Gender == Gender.Man;\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<p>Sadece kad\u0131n olan ve 18 ya\u015f\u0131ndan b\u00fcy\u00fck olan ki\u015fileri temsil eden specification;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nclass WomenAndOver18YearsOldOnlySpecification : Specification&lt;Person&gt;\r\n{\r\n    public override Expression&lt;Func&lt;Person, bool&gt;&gt; ToExpression()\r\n    {\r\n        return p =&gt; p.Gender == Gender.Woman &amp;&amp; p.Age &gt; 18;\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<p>2020&#8217;den \u00f6nce \u00fcretilmi\u015f ve 18 ya\u015f\u0131n \u00fczerindeki ki\u015fileri temsil eden specification;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nclass PersonsOver18YearsOfAgeManufacturedBefore2020 : Specification&lt;Person&gt;\r\n{\r\n    public override Expression&lt;Func&lt;Person, bool&gt;&gt; ToExpression()\r\n    {\r\n        return p =&gt; p.CreatedDate.Year &lt;= 2020 &amp;&amp; p.Age &gt; 18;\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<p>\u0130\u015fte bu kadar&#8230; Olu\u015fturulan bu specification&#8217;lar\u0131 isterseniz query yahut validation i\u015flemleri s\u0131ras\u0131nda kullanabilir ve b\u00f6ylece i\u015f kurallar\u0131n\u0131 uygulama \u00e7ap\u0131nda hem anlamsal olarak daha verimli kullanabilir hem de merkezi bir noktadan y\u00f6netilebilir k\u0131labilirsiniz.<\/p>\n<h4>Composite Specification<\/h4>\n<p>Olu\u015fturulan specification&#8217;lar\u0131 <code>AND<\/code>, <code>OR<\/code> ve <code>NOT<\/code> gibi mant\u0131ksal operat\u00f6rler e\u015fli\u011finde birle\u015ftirerek kullanmaya <em>Composite Specification<\/em> denmektedir. Bunun i\u00e7in a\u015fa\u011f\u0131daki gibi bu mant\u0131ksal operat\u00f6rlere kar\u015f\u0131l\u0131k \u00f6zel specification s\u0131n\u0131flar\u0131 tan\u0131mlanabilir.<\/p>\n<p>T\u00fcm s\u0131n\u0131flarda ortak kullan\u0131laca\u011f\u0131 i\u00e7in \u00f6ncelikle a\u015fa\u011f\u0131daki &#8216;ParameterReplacer&#8217; s\u0131n\u0131f\u0131 tan\u0131mlanmal\u0131d\u0131r;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nclass ParameterReplacer : ExpressionVisitor\r\n{\r\n    private readonly ParameterExpression _parameter;\r\n    public ParameterReplacer(ParameterExpression parameter)\r\n        =&gt; _parameter = parameter;\r\n    protected override Expression VisitParameter(ParameterExpression node)\r\n        =&gt; base.VisitParameter(_parameter);\r\n}\r\n<\/pre>\n<\/div>\n<p>Burada <code>ParameterExpression<\/code> ile constructor&#8217;dan gelen parametre temsil edilmekte ve &#8216;VisitParameter&#8217; metodu ile bu parametrenin i\u015flevi devreye sokulmaktad\u0131r. Hakl\u0131 olarak <strong><em>neye yarayacak bu hoca la?<\/em><\/strong> diye sordu\u011funuzu duyar gibiyim&#8230; Bizler bu parametre ile olu\u015fturaca\u011f\u0131m\u0131z <code>AND<\/code>, <code>OR<\/code> ve <code>NOT<\/code> mant\u0131ksal \u00e7al\u0131\u015fmalar\u0131n\u0131 temsil edecek ve \u00f6nc\u00fcl olan specification&#8217;lar\u0131m\u0131z ile birle\u015ftirece\u011fiz.<\/p>\n<p>\u015eimdi devam edince daha da net anla\u015f\u0131lacakt\u0131r kanaatindeyim. Hi\u00e7 vakit kaybetmeden s\u0131ras\u0131yla operat\u00f6rlere kar\u015f\u0131l\u0131k specification&#8217;lar\u0131 in\u015fa edelim.<\/p>\n<p><em>And<\/em> i\u00e7in;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nclass AndSpecification&lt;T&gt; : Specification&lt;T&gt;\r\n{\r\n    private readonly Specification&lt;T&gt; _left;\r\n    private readonly Specification&lt;T&gt; _right;\r\n\r\n    public AndSpecification(Specification&lt;T&gt; left, Specification&lt;T&gt; right)\r\n    {\r\n        _left = left;\r\n        _right = right;\r\n    }\r\n\r\n    public override Expression&lt;Func&lt;T, bool&gt;&gt; ToExpression()\r\n    {\r\n        Expression&lt;Func&lt;T, bool&gt;&gt; leftExpression = _left.ToExpression();\r\n        Expression&lt;Func&lt;T, bool&gt;&gt; rightExpression = _right.ToExpression();\r\n\r\n        ParameterExpression parameterExpression = Expression.Parameter(typeof(T));\r\n        BinaryExpression andExpression = Expression.AndAlso(leftExpression.Body, rightExpression.Body);\r\n        andExpression = (BinaryExpression)new ParameterReplacer(parameterExpression).Visit(andExpression);\r\n\r\n        return Expression.Lambda&lt;Func&lt;T, bool&gt;&gt;(andExpression, parameterExpression);\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<p><em>Or<\/em> i\u00e7in;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nclass OrSpecification&lt;T&gt; : Specification&lt;T&gt;\r\n{\r\n    private readonly Specification&lt;T&gt; _left;\r\n    private readonly Specification&lt;T&gt; _right;\r\n\r\n    public OrSpecification(Specification&lt;T&gt; left, Specification&lt;T&gt; right)\r\n    {\r\n        _left = left;\r\n        _right = right;\r\n    }\r\n\r\n    public override Expression&lt;Func&lt;T, bool&gt;&gt; ToExpression()\r\n    {\r\n        Expression&lt;Func&lt;T, bool&gt;&gt; leftExpression = _left.ToExpression();\r\n        Expression&lt;Func&lt;T, bool&gt;&gt; rightExpression = _right.ToExpression();\r\n\r\n        ParameterExpression parameterExpression = Expression.Parameter(typeof(T));\r\n        BinaryExpression orExpression = Expression.OrElse(leftExpression.Body, rightExpression.Body);\r\n        orExpression = (BinaryExpression)new ParameterReplacer(parameterExpression).Visit(orExpression);\r\n\r\n        return Expression.Lambda&lt;Func&lt;T, bool&gt;&gt;(orExpression, parameterExpression);\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<p><em>NotEqual<\/em> i\u00e7in;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nclass NotEqualSpecification&lt;T&gt; : Specification&lt;T&gt;\r\n{\r\n    private readonly Specification&lt;T&gt; _left;\r\n    private readonly Specification&lt;T&gt; _right;\r\n\r\n    public NotEqualSpecification(Specification&lt;T&gt; left, Specification&lt;T&gt; right)\r\n    {\r\n        _left = left;\r\n        _right = right;\r\n    }\r\n\r\n    public override Expression&lt;Func&lt;T, bool&gt;&gt; ToExpression()\r\n    {\r\n        Expression&lt;Func&lt;T, bool&gt;&gt; leftExpression = _left.ToExpression();\r\n        Expression&lt;Func&lt;T, bool&gt;&gt; rightExpression = _right.ToExpression();\r\n\r\n        ParameterExpression parameterExpression = Expression.Parameter(typeof(T));\r\n\r\n        BinaryExpression notEqualExpression = Expression.NotEqual(leftExpression.Body, rightExpression.Body);\r\n        notEqualExpression = (BinaryExpression)new ParameterReplacer(parameterExpression).Visit(notEqualExpression);\r\n\r\n        return Expression.Lambda&lt;Func&lt;T, bool&gt;&gt;(notEqualExpression, parameterExpression);\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<p><em>Equal<\/em> i\u00e7in;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nclass EqualSpecification&lt;T&gt; : Specification&lt;T&gt;\r\n{\r\n    private readonly Specification&lt;T&gt; _left;\r\n    private readonly Specification&lt;T&gt; _right;\r\n\r\n    public EqualSpecification(Specification&lt;T&gt; left, Specification&lt;T&gt; right)\r\n    {\r\n        _left = left;\r\n        _right = right;\r\n    }\r\n\r\n    public override Expression&lt;Func&lt;T, bool&gt;&gt; ToExpression()\r\n    {\r\n        Expression&lt;Func&lt;T, bool&gt;&gt; leftExpression = _left.ToExpression();\r\n        Expression&lt;Func&lt;T, bool&gt;&gt; rightExpression = _right.ToExpression();\r\n\r\n        ParameterExpression parameterExpression = Expression.Parameter(typeof(T));\r\n\r\n        BinaryExpression equalExpression = Expression.Equal(leftExpression.Body, rightExpression.Body);\r\n        equalExpression = (BinaryExpression)new ParameterReplacer(parameterExpression).Visit(equalExpression);\r\n\r\n        return Expression.Lambda&lt;Func&lt;T, bool&gt;&gt;(equalExpression, parameterExpression);\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<p>Mant\u0131ksal operat\u00f6rler i\u00e7in olu\u015fturdu\u011fumuz specification&#8217;lara g\u00f6z atarsan\u0131z e\u011fer hepsinde ortak i\u015f olarak, composite bir \u015fekilde ilgili operat\u00f6r ile birle\u015ftirilecek specification&#8217;lar sol(_left) ve sa\u011f(_right) olmak \u00fczere temsil edilmekte ve &#8216;ToExpression&#8217; metodu i\u00e7erisinde de bu specification&#8217;lar expression&#8217;a d\u00f6n\u00fc\u015ft\u00fcr\u00fclmektedirler. Akabinde her birinde <code>Expression.Parameter<\/code> ile bir parametre olu\u015fturulmakta ve mant\u0131ksal operat\u00f6re g\u00f6re specification&#8217;lar davran\u0131\u015fa tabi tutulmakta ve bu davran\u0131\u015f <code>BinaryExpression<\/code> t\u00fcr\u00fcnden elde edilmektedir. Ve son olarak ilk ba\u015fta olu\u015fturdu\u011fumuz <code>ParameterReplacer<\/code> s\u0131n\u0131f\u0131 ile, <code>Expression.Parameter<\/code> komutu e\u015fli\u011finde \u00fcretilen parametreye bu davran\u0131\u015f &#8216;Visit&#8217; metodu ile ge\u00e7irilmekte ve &#8216;Lambda&#8217; fonksiyonu ile davran\u0131\u015f i\u015flenmektedir.<\/p>\n<p>Tabi bu operat\u00f6rlere \u00f6zel \u00e7al\u0131\u015fmalardan sonra a\u015fa\u011f\u0131daki gibi <code>Specification&lt;T&gt;<\/code> s\u0131n\u0131f\u0131nda da ilgili operat\u00f6rlere kar\u015f\u0131l\u0131k fonksiyonlar tan\u0131mlanmal\u0131d\u0131r ki her bir specification \u00fczerinden bir sonraki istenilen mant\u0131kta \u00e7a\u011fr\u0131labilsin.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nabstract class Specification&lt;T&gt;\r\n{\r\n    public abstract Expression&lt;Func&lt;T, bool&gt;&gt; ToExpression();\r\n    public bool IsSatisfiedBy(T entity)\r\n    {\r\n        Func&lt;T, bool&gt; predicate = ToExpression().Compile();\r\n        return predicate(entity);\r\n    }\r\n\r\n    public Specification&lt;T&gt; And(Specification&lt;T&gt; specification)\r\n        =&gt; new AndSpecification&lt;T&gt;(this, specification);\r\n    public Specification&lt;T&gt; Or(Specification&lt;T&gt; specification)\r\n        =&gt; new OrSpecification&lt;T&gt;(this, specification);\r\n    public Specification&lt;T&gt; NotEqual(Specification&lt;T&gt; specification)\r\n        =&gt; new NotEqualSpecification&lt;T&gt;(this, specification);\r\n    public Specification&lt;T&gt; Equal(Specification&lt;T&gt; specification)\r\n        =&gt; new EqualSpecification&lt;T&gt;(this, specification);\r\n}\r\n<\/pre>\n<\/div>\n<p>Ard\u0131ndan ister in-memory&#8217;de isterseniz de a\u015fa\u011f\u0131daki gibi <code>IQueryable<\/code> sorgular\u0131nda rahatl\u0131kla kullan\u0131labilmektedir.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nApplicationDbContext context = new();\r\n\r\nWomenAndOver18YearsOldOnlySpecification s1 = new();\r\nPersonsOver18YearsOfAgeManufacturedBefore2020 s2 = new();\r\nPeopleFitForTheJobSpecification s3 = new();\r\nOnlyMenSpecification s4 = new();\r\n\r\nvar query = context.Persons\r\n    .Where(s1.And(s2)\r\n             .Or(s3)\r\n             .And(s4)\r\n             .ToExpression()\r\n          )\r\n    .ToQueryString();\r\nConsole.WriteLine(query);\r\n<\/pre>\n<\/div>\n<p>\u00d6zellikle <code>IQueryable<\/code> sorgular\u0131nda \u00fcretilecek query&#8217;leri merak edebilme ihtimalinizden dolay\u0131 yukar\u0131daki \u00f6rnek \u00e7al\u0131\u015fman\u0131n \u00e7\u0131kt\u0131s\u0131na g\u00f6z atmakta fayda vard\u0131r.<br \/>\n<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2022\/11\/C-Ile-Specification-Patterni-Inceleyelim.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2022\/11\/C-Ile-Specification-Patterni-Inceleyelim.png\" alt=\"C# \u0130le Specification Pattern&#039;\u0131 \u0130nceleyelim\" width=\"798\" height=\"263\" class=\"aligncenter size-full wp-image-25901\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2022\/11\/C-Ile-Specification-Patterni-Inceleyelim.png 798w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2022\/11\/C-Ile-Specification-Patterni-Inceleyelim-300x99.png 300w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2022\/11\/C-Ile-Specification-Patterni-Inceleyelim-768x253.png 768w\" sizes=\"auto, (max-width: 798px) 100vw, 798px\" \/><\/a><\/p>\n<p>Nihai olarak;<br \/>\nUygulama baz\u0131nda domain bilgileri do\u011frultusunda genel kabul g\u00f6rm\u00fc\u015f i\u015f kurallar\u0131n\u0131 specification pattern ile merkezi bir noktaya ta\u015f\u0131yarak daha y\u00f6netilebilir bir hale getirmenin avantajlar\u0131n\u0131 m\u00fcmk\u00fcn mertebe g\u00f6zlemlemeye \u00e7al\u0131\u015fm\u0131\u015f bulunuyoruz. \u00d6zellikle bu yap\u0131lanman\u0131n, <code>IQueryable<\/code> sorgular\u0131n\u0131 repository s\u0131n\u0131flar\u0131 \u00fczerinden daha esnek bir \u015fekilde \u00fcretebilmemizi sa\u011flayacak bir olgunluk getirdi\u011fine dair sizlerin de kanaati vard\u0131r diye d\u00fc\u015f\u00fcn\u00fcyorum.<\/p>\n<p>\u0130lgilenenlerin faydalanmas\u0131 dile\u011fiyle&#8230;<br \/>\nSonraki yaz\u0131lar\u0131mda g\u00f6r\u00fc\u015fmek \u00fczere&#8230;<br \/>\n\u0130yi \u00e7al\u0131\u015fmalar&#8230;<\/p>\n<p>Not : Specification Pattern&#8217;a dair ve \u00f6zellikle Composite Specification i\u00e7in yapm\u0131\u015f oldu\u011fumuz \u00f6rnek \u00e7al\u0131\u015fmay\u0131 a\u015fa\u011f\u0131daki github adresinden inceleyebilirsiniz.<br \/>\n<a href=\"https:\/\/github.com\/gncyyldz\/Specification_Pattern_Example\" rel=\"noopener\" target=\"_blank\">https:\/\/github.com\/gncyyldz\/Specification_Pattern_Example<\/a><\/p>\n<!-- AddThis Advanced Settings generic via filter on the_content --><!-- AddThis Share Buttons generic via filter on the_content -->","protected":false},"excerpt":{"rendered":"<p>Merhaba, Bu i\u00e7eri\u011fimizde, domain i\u00e7erisinde i\u015f kurallar\u0131n\u0131 kaps\u00fclleyerek kod taban\u0131nda farkl\u0131 noktalarda yeniden kullan\u0131labilir k\u0131lmam\u0131z\u0131 ve b\u00f6ylece bu kurallar\u0131 tek bir yerden merkezi olarak y\u00f6netmemizi sa\u011flayan Specification modelini ele alacak ve detayl\u0131 bir \u015fekilde&#46;&#46;&#46;<!-- AddThis Advanced Settings generic via filter on get_the_excerpt --><!-- AddThis Share Buttons generic via filter on get_the_excerpt --><\/p>\n","protected":false},"author":1,"featured_media":25905,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[153],"tags":[4726,750,4725],"class_list":["post-25885","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-design-pattern","tag-composite-specification","tag-design-pattern","tag-specification-pattern"],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts\/25885","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/comments?post=25885"}],"version-history":[{"count":17,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts\/25885\/revisions"}],"predecessor-version":[{"id":25906,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts\/25885\/revisions\/25906"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/media\/25905"}],"wp:attachment":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/media?parent=25885"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/categories?post=25885"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/tags?post=25885"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}