GDG Devfest 2019 – Edirne

Devfest 2019 serüveninde bir sonraki adım Edirne oldu.

Geçtiğimiz Pazar günü gerçekleşen etkinlikte, Yapay Zeka’nın Temelleri konulu 1 saatlik bir sunum gerçekleştirme fırsatı buldum.

Öncelikle kibar davetleri için GDGEdirne ekibine teşekkür ederim.

Konu bir önceki etkinlikteki ile aynı da olsa bu kez dinleyici kitlesi tamamen üniversite öğrencisi ve büyük çoğunluğu mühendislik okuyan genç arkadaşlardan oluşuyordu. Hal böyle olunca sunumun dili de içeriği de biraz daha farklı oldu.

Edirne’ye ve Trakya Üniversitesi’ne ilk ziyaretimdi. Hem şehirden, hem üniversiteden hem de öğrenci arkadaşlarla vakit geçirmekten oldukça keyif duydum. Etkinlikte emeği geçen herkesin ellerine sağlık.

Paylaşmak istediğim bir önemli nokta, şimdiye kadarki sohbetlerimiz ve bana mesaj ya da mail ile ulaşan neredeyse tüm arkadaşların derdi/sorusu benzer. Kendi işini kurma hayali…

Her ne kadar objektif bir şekilde iyisini kötüsünü aktarmaya, görüş ve düşüncelerini etkilemeden bildiklerimi paylaşmaya çalışsam da sonrasında gelen sorular beni biraz ürkütüyor ne yalan söyleyeyim.

Pek çoğu kurumsal hayatta bir iş bulamayacağı düşüncesi ile kendi işini kurmayı hedefliyor. Bir kısmı ise çok iyi bir fikri olduğuna inanarak kendini geliştirmek yerine fikrine yatırım yapmaya niyetli.

Tüm duygusal devinimleri, korkuları/kaygıları ve toplumun dayattığı ezberleri bir kenara bırakalım.

İş bulmanın, yani bir işe alınmanın matematiksel karşılığını bir düşünelim.

Bir işletme, sizin deneyiminizi ve bilginizi kullanarak sizin üzerinizden size verdiğinden daha fazla (çok daha fazla) para kazanabiliyorsa, sizi işe alır.

Bu denklemde korkuyu tam olarak nereye koyuyoruz?

İşletme bulamama korkusu mu?

Yeterince deneyim ve bilgi sahibi olmama korkusu mu?

Aldığınız paradan daha fazlasını kazandıramama korkusu mu?

Hiç biri olduğunu sanmıyorum. Ama yine de teoriyi ispatlayalım.

İşletme bulamamak, bölgesel düşündüğünüzde bir problem olabilir. Ama güzel ülkemizin hiçbir yerinde sizi işe alacak bir işletmenin kalmamış olması ihtimali bana pek gerçekçi gelmiyor. Öyleyse ilk olarak göç etmekten, doyduğumuz yere yuva demekten korkmamak gerekiyor.

Yeterince deneyim ve bilgi sahibi değilseniz ve bir işletme bu yüzden sizi işe almayacaksa, bunun da çözümü basit. Deneyim ve bilgi edinmeye başlayın. Hap gibi söyleyince insanı kızdırıyor ama bunun bahanesi yok. Gözleriniz ağrıyana, dirsekleriniz nasır tutana kadar çalışmadan, maalesef bilgi ve deneyim edinmek mümkün değil. Eğer ki konu “ben böyle zora gelemem” ise, o zaman kendi işini kurmak bu yaklaşıma verilecek son reçete…

Aldığınız paradan daha fazlasını kazandıramama ise mevzu, bu da bir çalışanın kendisine dert edineceği bir konu değil. Eğer ki siz şirkete size verdiklerinden daha fazlasını kazandıramıyorsanız, bütün suç şirkettedir. İşi bilmiyordur, ürünü satamıyordur, sizin bilgi ve deneyiminizi ederine pazarlayamıyordur. O şirkette zaten çalışmayın.

Özetle bu konuda diyeceğim, denemeden peşin hükümlü olup sizden daha az hak edenlerin önünü böyle kolay açmayın. Özellikle iş hayatında oyun siz pes ettiğinizde bitiyor.

Bu kadar kolay pes etmeyin.

Bir başka etkinlikte daha mutlu ve umutlu görüşmek dileğiyle…

Refactoring 101: DDD & DI

Yeni yetme öğrencilik yıllarımda kodladığım, arşivlerimin derinliklerinden şans eseri bulduğum neredeyse 10 yaşındaki IndexMaker projesini çağa uyarlamaya son hız devam ediyoruz.

Bir önceki yazıda Isolation üzerinde durmuştuk. Ne ile Nasıl ayrımını yapmak üzerine biraz ısınmıştık.

Şimdi Ne ile Nasıl’ı neredeyse tamamen birbirinden koparmak ve nihayetinde yalın, çevik ve modüler bir proje oluşturmak için domain yönelimli tasarım ve geliştirme konusunu masaya yatıracağız.

Nedir bu DDD?

Şu resim çok açıklayıcı olacaktır diye tahmin ediyorum;

Geleneksel Mimari’ye karşı DDD Soğan Mimari

Bu resmin özeti şudur, Domain’ime dokunma!

Şaka bir yana, nedir ne değildir aktarabilmek için şöyle bir kıyaslamaya başlayalım.

Geleneksel mimaride (sol), veritabanı teknolojisi, kullanılan üçüncü parti eklenti/kütüphane ve araçlar gibi projenin çevresel bileşenleri tüm katmanlardan erişilebilir durumdadır.

Bu zamana kadar hem hızlı geliştirmeye imkan sağladığı için hem de işleri başlangıçta kolaylaştırdığı için bu yaklaşım benimsenmiştir.

Ancak günümüz yazılım projeleri artık öyle küçük ve basit projeler değil ne yazık ki. Haliyle böyle bir altyapı çok ciddi sorunlara yol açabiliyor.

Örneğin en basitinden kullandığınız veritabanı teknolojisini değiştirmek isteseniz, kolları sıvayıp projenin tüm katmanlarında uzun soluklu bir gezintiye çıkmanız gerekiyor.

Ayrıca bu yaklaşımla genişlemesi, ölçeklenmesi ve bakımı zor projeler ortaya çıkıyor ki bu da yine bu devirde kabul edilemez bir problem.

Peki geleneksel mimarideki bu sorunlara DDD ile nasıl çözüm buluyoruz?

DDD, işleri gerçek hayatta olduğu gibi ele almayı savunuyor. Adında geçen domain, yapacağınız işin özünü temsil ediyor aslında.

Bir yazılım projesi hiç bir zaman bir “yazılım projesi” olsun diye geliştirilmez. Ya bir doküman yönetim sistemi, ya bir otel rezervasyon sistemi, ya da kaynak yönetim sistemi gibi süslü isimleri olur.

Daha da ötesinde, bir yazılım projesinin tam anlamıyla hangi problemi çözeceği ve kimler için bu problemi çözüyor olduğu sorularının cevapları bize Domain’i verir. Cevapta yer alan her bir eleman da aslında Domain’in elemanlarıdır.

Index Maker projesi için Domain nedir?

Index Maker, belirlediğiniz bir klasörü arayarak içindeki tüm dosya ve klasörleri size listeleyen ve içerisinde arama yapmanıza izin veren bir yazılım projesi. Bu tanımdan hareketle Index Maker’ın Domain elemanları neler olmalı?

  • Dosya
  • Klasör
  • Dizin Gezici
  • Dizin/Dosya Arama Motoru

Bu projeden beklediğim her şeyi karşılayan elemanlar bunlar.

Bunları sağda gördüğünüz o soğanın en içine gömeceğiz çünkü bu projenin işlevi tamamen değişmediği sürece yani yukarıda yazdığım tanımda ciddi bir değişiklik olmadığı sürece bu elemanlar sabit kalacak.

Şimdi gelelim bu elemanları yani Domain’i çevreleyecek diğer katmanlara. Evet bir dosya ve klasör elemanı olacağından bahsettik. Ve belirlenen bir klasör elemanı içerisinde gezip bize tüm alt klasör ve dosyaları listeleyecek bir gezici sınıf olmalı.

Bir dakika durup düşünelim.

Bu gezici sınıfı domaine dahil edersek ne olur?

Bir dizin gezgini yazmak teknoloji bağımlı bir iş. Yani teknolojinin değişimiyle değişmesi çok muhtemel. Bu sebeple dizin gezginini domaine dahil etmek doğru değil. Ancak kendisi domain’in bir elemanı/gereksinimi. Yani domain’i tanımlayan elemanlardan biri. Haliyle domain’den çıkarmamız da uygun değil.

Ne yapacağız peki?

Keşke gereksinimleri ve fonksiyonelliği tanımlayabileceğimiz ama implementasyonu başka bir yerde yapabileceğimiz bir teknoloji olsa değil mi?

DI’a girmeye başlıyoruz, kemerleri bağlayın.

Dizin gezgininin limitlerini domainde belirlemek için dizin gezginini ifade eden bir interface oluşturacağız. Infrastructure hariç tüm yazılım dizin gezgininin yalnızca interface’ini bilecek. Böylece teknolojik detaylar işin içine girmeden dizin gezgininin tüm özelliklerini kullanabileceğiz.

Sonrasında Autofac ile bu interface ile Infrastructure katmanında yazdığımız ve teknoloji içeren Dizin Gezgini’ni register edeceğiz.

Bu sayede NE’yi ve NASIL’ı birbirinden tamamen ayırmış olduk.

Mevcut dizin gezgini windows dosya dizinlerinde arama yapmak üzere geliştirildi. Ama DDD ile geliştirme yaptığımız için, şu an desek ki bunu Linux işletim sistemlerinde de çalışacak hale getirelim (.Net Core’a geçtiğimizi varsayalım) bunu yapmak için değiştirmemiz gereken tek proje Infrastructure projesi olacak. Kod karmaşası içerisinde referans aramalar, bir değişiklik yüzünden hata veren absürt sınıflar yok!

Tamamsak koda bir göz atalım.

Önce proje yapısının yeni halini görelim;

Index Maker – DDD

1 projeden oluşan yazılımımız 3 projeye bölündü. Eskiden yalnızca bir Winforms projesinden oluşuyor iken şimdi;

  1. IndexMaker.App – WinForms projesi
  2. IndexMaker.Domain – Domain’i temsil eden Class Library
  3. IndexMakar.Infrastructure – Infra’yı temsil eden Class Library

Şimdi sırasıyla projelerin içinde neler olduğunu inceleyelim.

IndexMaker.App – WinForms Projesi

Çok temiz değil mi? Yalnızca bir form dosyası, hepsi bu.

Diğer tüm dependency’ler referans verilen projelerden geliyor.

Şimdi Domain’i inceleyelim.

Domain projesi için temelde kullandığım 4 ana klasör var.

  • Constants
    Domain’e özgü constant değişkenleri ve sınıfları tanımladığım klasör.
  • Entities
    Domain’de yer alan her bir entity’yi yani domain’e özgü varlıkları karşılayan sınıfları tanımladığım klasör.
  • Repositories
    Veriye erişim ve manipulasyon için kullanılacak repository sınıflarını tanımladığım klasör. Tıpkı dizin gezgininde olduğu gibi repository sınıflarında da yalnızca interface oluşturup implementasyonu Infrastructure katmanında gerçekleştireceğiz.
  • Services
    Uygulama içerisinde domain’e özgü servislerin (dizin gezgini dahil) interface’lerinin yer aldığı klasör. Implementasyon nerede? Infra’da…

Şimdi de infra projesine bir göz atalım.

IndexMaker.Infrastructure – Class Library

Buradaki klasör yapısı da;

  • Data
    Veriye erişim ve veriyi yönetmek ile ilgili tüm teknoloji bağımlı fonksiyonelliği tanımladığım klasör. Altındaki Entities klasörü DB tarafında yer alacak entity’leri karşılıyor. EF klasörü ise bu proje için şimdilik eklenmemiş de olsa ilerleyen süreçte ekleyeceğimiz DBContext gibi EntityFramework sınıflarını tanımladığım klasör.
  • Services
    Domain’de bahsettiğimiz Services klasöründe yer alan interface’lerin implementasyonlarının yer aldığı klasör. Görebileceğiniz gibi Dizin gezgini sınıfımız da burada.

Form uygulamasındaki Program.cs dosyasına da bir göz atalım.

IndexMaker.App Program.cs

Burada daha önce bahsettiğim tüm Registration’ları gerçekleştireceğiz. Bunun için Autofac kütüphanesini kullanacağız. Nuget paketi olarak NPM ile edinebilirsiniz.

Buradaki mantık da kabaca şöyle. Dikkat ederseniz MainForm.cs form sınıfımız constructor’ında bir interface alıyor. Ancak bu sınıfı ilkleyen bir başka sınıf mevcut değil. Peki nasıl oluyor da bu sınıf ilkleniyor?

ConfigureDependencies() metodunu incelediğimizde builder üzerinden çağırılmış bir RegisterType() komutunun builder’a “IDirectoryManagementService interface’ini gördüğünde DirectoryManagerService sınıfından bir instance yarat ve onu kullan” dediğini görebiliyoruz.

Ayrıca MainForm sınıfının constructor’ında bir parametre alacak şekilde değişiklik yaptığımız için artık eski usül form oluşturamıyoruz. Oluşturabiliriz, ancak bu durumda DirectoryManagerService sınıfından bir instance yaratıp bunu MainForm()’a geçirmemiz gerekecek ki bu hareket ile de Dependency Inject etmiş olmayacak, dependency kullanmış olacağız.

Bu sebeple MainForm sınıfını da builder’ımıza register ediyoruz. (Şu cümledeki kelimelerin yarısı İngilizce olmasına rağmen cümlenin Türkçe olması sinir bozuyor biliyorum, ama tümünü Türkçe yazınca da hiç bir şey anlaşılmıyor)

Bu işlem sayesinde artık MainForm’dan bir instance yaratma işi de builder’a aktarılmış durumda. Ve builder IDirectoryManagerService interface’ini gördüğünde DirectoryManagerService sınıfından bir instance yaratıp onu kullanması gerektiğini bildiği için sorunsuz bir şekilde formu kullanabiliyoruz.

Tüm kaynak kodlar github’da DDD klasörü altında mevcut.

Linki de buraya bırakayım: https://github.com/ycansener/IndexMaker

Sorular için Twitter’dan ya da comment ile bana ulaşabilirsiniz.

Ortasından başlayan ve merak edenler için bir önceki yazıda Isolation nedir? Neden yapılır? Nasıl yapılır? demiştik.

Buradan erişilebilir: http://blog.ycansener.com/?p=21

Bir sonraki yazıda işin içine bir de EF ve Code First yaklaşım ile bir veri tabanı ekleyelim diyorum.

Kısmet.

GDG DevFest 2019 – Konya

Yapay zeka konusunda ezberleri bozmak ve olaylara farklı açılardan da bakabilmemizi sağlamak adına çıktığım mini serüvende bu kez de yolum Konya’ya düştü.

GDG DevFest 2019 serisinin ilk ayağı bu yıl Konya’daydı. Tabi ki konuşmacı olma şansını kaçırmadım.

İzlenim ve deneyimlerimi kısaca paylaşacağım bir post olsun. Hem de blogda bir izi kalsın gayesindeyim.

İlk olarak, uzun zamandır gerek konuşmacı gerek katılımcı olarak yer aldığım pek çok etkinlikten çok daha verimli ve besleyiciydi. Katılımcılardan konuşmacılara ve organizasyona kadar hepsi, özellikle Yapay Zeka gibi bir konuda işin felsefesinden bahsedecek olan benim gibi bir konuşmacının hayal edebileceği en ideal kitleydi diyebilirim.

Belediyenin ve organizasyon ekibinin ne kadar ilgili ve özverili çalıştığını etkinliğin her anında hissettim. Eminim dahil olan herkes benimle aynı fikirdedir. Hele ki Konya Bilim Merkezi‘nin imkanları… Ben size bir görsel bırakayım, gerisini siz tahayyül edin.

Katılımcıların yaklaşımları karşısında diyebileceğim, katılımcıların gerçekten ilgili olan ve etkinliğin hakkını vermeye gelen kişilerden oluştuğu her hallerinden belliydi.

Kendi sunumum özelinde hem değerlendirme hem de öz-eleştiri yapacak olursam; Yapay Zeka’nın Temelleri konusu bir teknoloji ya da yöntem aktarımından çok işin felsefesine, nelerin doğru ve yanlış algılandığı ve uygulandığına değinen biraz da sitem barındıran bir konu benim için.

Sunum boyunca hem sözlükten bilgileri taşımak ve tartışmak, hem güncel problemlere ve YZ ile çözümlerine değinmek, hem de yapay zeka girişimciliği özelinde deneyim paylaşımlarında bulunmak üzerine bir üçgen kurarak dinleyenleri sıkmadan kafalarını da çok fazla yakmadan makineyi biraz yağlamaya çalışıyorum diyelim. Dilerim amacına ulaşıyordur.

Etkinlikte emeği geçen başta sevgili Merve Dündar ve Dilan Balcı‘ya, devamında tüm GDG Konya ekibine gönülden teşekkürler.

Sunumu merak edenler, @gdgkonya twitter hesabı üzerinden ekibe mesaj atarak sadece benim değil, bildiğim kadarıyla etkinlikte yapılmış olan tüm sunumlara erişebilirler.

Sorular ve yorumlar için yine twitter üzerinden bana ulaşabilir ya da bu post’a yorum bırakabilirsiniz.

Bir başka etkinlikte yine kafaları yakmak dileğiyle…

Refactoring 101: Isolation

İlk adım, izolasyon.

Neleri izole etmeliyiz?

Bu soruya cevap verirken dikkat edeceğimiz birkaç nokta var.

  1. Her bir metodun/sınıfın/projenin tek bir çalışma amacı olmalı, ve tek bir amaç için değiştirilmeli/güncellenmeli/genişletilmeli. (SOLID -> S -> Single Responsibility Principle)
  2. Front-end ile back-end’i birbirinden olabildiğince ayırmalıyız. Bu sebeple formlar içerisindeki metotlar ile işin kendisini yapan back-end kodlarını izole etmeliyiz.
    Neye göre karar vereceğiz derseniz, bu adım için kolay bir yöntem var. Front-end ile back-end’i birbirinden ayırırken her zaman kendinize “minimum efor ve back-end’de minimum değişiklik ile yeni bir front-end projesi oluşturabiliyor muyum?” diye sorabilirsiniz. (Örneğin bir web client projeniz var ise onun yanına bir mobil client projesi eklemek gibi düşünebilirsiniz.) Cevap evet olana kadar da ara kütüphaneler ve sınıflar ile izolasyona devam etmelisiniz.
  3. Ne ve Nasıl sorularının cevapları birbirinden izole olmalı. Domain Driven Design konusunda belki de en çok başvurmanız gereken sorudur Ne? sorusu. Bir örnekle açıklayayım.
IEnumerable<string> GetUserNames();

Bu metot NE yapıyor? diye sorduğunuzda vereceğiniz cevap “Tüm kullanıcıların isimlerini getiriyor” olmalı. Ve içeriğindeki kodu incelediğinizde eğer vereceğiniz cevap “EntityFramework ile MyTestDB veritabanına bağlanıp Users tablosu üzerinden User DBSet’ini çekip, bir döngü içerisinde yeni bir string listesine kullanıcı adlarını yazmak ve bunu return etmek.” ise, vay halinize…

Ne sorusuna vereceğiniz cevap hiçbir zaman teknoloji ya da yöntem içermemeli. Benzer şekilde nasıl sorusu sorduğumuz bir metot da Ne sorusunun cevabını verememeli.

Bu bilgiler ışığında projenin ilk versiyonu üzerinde izolasyon çalışmalarına başlayalım.

Arayüz üzerinde herhangi bir değişiklik yapmamaya çalışacağım gerekmedikçe.

Mevcutta arayüzün geldiği nokta şu şekilde:

Mühendislerin dostu, ajansların korkulu rüyası “Çirkin ama işlevsel arayüz”

Formun code-behind tarafında ne gibi değişiklikler olduğu da proje yapısındaki değişimden görülebilir.

Yeni proje yapısı

Helpers ve Models isminde iki yeni klasör eklendi. Yalnızca bu ekran görüntüsünden artık Windows’un kendi File ve Folder sınıflarını kullanmadığımızı, kendimize ait yeni birer FileModel ve FolderModel sınıfı oluşturduğumuzu görebiliriz. Bu iki model sınıfı da ortak bir IDirectoryItem interface’inden türüyor. Gerçek dünyada olduğu gibi nasıl ki File ve Folder bir dosya sisteminin iki elemanı ise, kodda da bu yapıyı kurmalıyız. Domain Driven Design’a yaklaştıkça bu eğilimi daha net görebileceksiniz.

Bir diğer yenilik, DirectoryManager sınıfı. Buradan da anlıyoruz ki dizinler ile ilgili işlemleri yönetmek üzere bir yardımcı sınıf oluşturulup tüm fonksiyonellik buraya taşınmış.

Şimdi forma ait code-behind’a bir göz atalım ve nelerin izole olduğunu görelim.

Eski ve yeni kodları arka arkaya bölüm bölüm ekleyerek nelerin neden değiştiğini aktaracağım.

Eski Form1.cs

FolderBrowserDialog fbd;
        string path;
        string[] folders;
        string[] files;
        public Form1()
        {
            InitializeComponent();
            toolTip1.SetToolTip(listBox1, "Bulunduğu dizini açmak için çift tıklayınız. İşlem menüsü için sağ tıklayınız.");
        }

        private void button1_Click(object sender, EventArgs e)
        {
            fbd = new FolderBrowserDialog();
            fbd.ShowDialog();
            if (fbd.SelectedPath.ToString() != "")
            {
                path = fbd.SelectedPath;
                textBox1.Text = path;
                folders = Directory.GetDirectories(path);
                files = Directory.GetFiles(path);
            }
        }

Yeni Form1.cs

FolderModel _folderModel;
        public Form1()
        {
            InitializeComponent();
            toolTip1.SetToolTip(treeViewResults, "Bulunduğu dizini açmak için çift tıklayınız. İşlem menüsü için sağ tıklayınız.");
        }

        private void buttonBrowse_Click(object sender, EventArgs e)
        {
            using (FolderBrowserDialog fbd = new FolderBrowserDialog())
            {
                DialogResult dr = fbd.ShowDialog();
                if (dr.Equals(DialogResult.OK))
                {
                    string path = fbd.SelectedPath;
                    string name = DirectoryManager.GetName(path);
                    textBoxPath.Text = path;
                    _folderModel = new FolderModel(name, path, null);
                }
            }
        }

İlk göze çarpan global değişkenler olmalı. Eski versiyonda dosyalar, klasörler, FolderBrowserDialog’a kadar pek çok global değişken vardı.

Tüm bunlar yerine yalnızca seçilen klasöre ait bilgileri tutan FolderModel sınıfından bir değişken yeterli.

Bir diğer iyileştirme, isimlendirmeler. button1 gibi bir isimlendirme yerine butonun yaptığı iş ile butonu ve butona ait metotları isimlendirmek kodun okunabilirliğini ve haliyle bakımını kolaylaştıracaktır.

Bir diğer yenilik, Disposable bir sınıf olan FolderBrowserDialog sınıfının kullanımı. Daha önce uygulamanın kapanışında manuel olarak dispose edilen bu sınıf artık browse butonunun click eventinde using ile initialize edilerek kullanılıyor ve görevini tamamladıktan sonra, yani bize seçilen folder’ın yolunu verdikten sonra otomatik olarak dispose ediliyor.

Bir diğer güzellik de, using içerisinde tanımladığımız tüm değişkenler görevini yerine getirdikten sonra dışarıya yalnızca _folderModel sınıfı çıkabiliyor. Ve bizim için gerekli olan tek sınıf bu. Diğer tüm referans ve değer değişkenlerinin ömrü sonlanıyor.

Gelelim klasörleri/dosyaları arama ve listeleme işlevselliğine.

Eski Kod

private void button2_Click(object sender, EventArgs e)
        {
            if (folders != null || files != null)
            {
                if (checkBox1.Checked)
                {
                    yaz(folders, 0);
                    yaz(files, 0);
                }
                else
                {
                    duzYaz(folders);
                    duzYaz(files);
                }
            }
        }

        private void yaz(string[] paths,int degree)
        {
            string newPath;
            string ayirac = "";
            for (int i = 0; i < paths.Length; i++)
            {
                for (int j = 0; j < degree; j++)
                    ayirac += "     ";
                listBox1.Items.Add(ayirac +"-"+ paths[i].ToString());
                newPath = paths[i].ToString();
                if (Directory.Exists(newPath))
                {
                    yaz(Directory.GetDirectories(newPath),degree+1);
                    yaz(Directory.GetFiles(newPath),degree+1);
                }
                ayirac = "";
            }
        }

        private void duzYaz(string[] paths)
        {
            string newPath;
            for (int i = 0; i < paths.Length; i++)
            {
                listBox1.Items.Add(paths[i].ToString());
                newPath = paths[i].ToString();
                if (Directory.Exists(newPath))
                {
                    duzYaz(Directory.GetDirectories(newPath));
                    duzYaz(Directory.GetFiles(newPath));
                }
            }
        }

Yeni Kod

private void buttonStartListing_Click(object sender, EventArgs e)
        {
            Clear();
            bool showCompletePath = checkBoxShowCompletePath.Checked;

            DirectoryManager dm = new DirectoryManager(_folderModel);
            dm.Investigate();

            FillTree(_folderModel, showCompletePath);
        }

        private void FillTree(FolderModel rootFolder, bool showCompletePath)
        {
            TreeNode rootNode = AddNode(null, rootFolder, showCompletePath);
            treeViewResults.Nodes.Add(rootNode);

            FillTree(rootNode, rootFolder, showCompletePath);
        }
        private void FillTree(TreeNode parentNode, FolderModel selectedFolder, bool showCompletePath)
        {
            TreeNode currentNode = AddNode(parentNode, selectedFolder, showCompletePath);

            foreach (var subFolder in selectedFolder.GetSubFolders())
            {
                FillTree(currentNode, subFolder, showCompletePath);
            }

            foreach (var file in selectedFolder.GetFiles())
            {
                AddNode(parentNode, file, showCompletePath);
            }
        }

        private TreeNode AddNode(TreeNode parentNode, IDirectoryItem itemToAdd, bool showCompletePath)
        {
            string nodeText = itemToAdd.Name;
            if (showCompletePath)
            {
                nodeText = itemToAdd.CompletePath;
            }

            if (parentNode != null)
                return parentNode.Nodes.Add(nodeText);

            return new TreeNode(nodeText);
        }

İlk değişiklik listView yerine TreeView kontrolünün kullanılması. Böylelikle eski kodda yer alan indent verme karmaşıklığı tamamen ortadan kalktı. Bir diğer önemli değişiklik tüm directory kodları ve fonksiyonelliğin DirectoryManager sınıfına taşınması. Bu aşamadaki en büyük izolasyon bu sınıf ile gerçekleşiyor.

Son olarak eski kodlarda iterative şekilde kodlanmış ağaç oluşturma fonksiyonları, yeni kodlarda recursive kodlanmış.

Bu aşamada hala tek bir form application üzerinden ilerliyoruz. Domain – Infrastructure – App üçgenini henüz kurmaya başlamadık. Ancak bu adımda yaptığımız izolasyon sayesinde bu ayrımı daha rahat görebiliyoruz artık.

Yeni eklenen modelleri ve DirectoryManager sınıfını github üzerinden inceleyebilirsiniz. Ayrıca bu aşamaya kadarki tüm kodlara yine github üzerindeki aynı repository’den, yazının başlığı ile aynı isimli klasör altında ulaşabilirsiniz.

Bir sonraki adımda Domain ve Infra ayrımını yapıp Inversion of Control ve Dependency Inversion’ı odağımıza alacağız. Ne ve Nasıl‘ı birbirinden ayırmaya devam edeceğiz.

Sıradaki yazı; Refactoring 101: DDD, IoC ve DI

Refactoring 101: IndexMaker

Lisans döneminden kalma ödev, not, proje dokümanlarını kurcalıyorum son birkaç gündür. Arşivimi temizlemek ve taşımak vesilesiyle giriştiğim bu yolda o kadar güzel içerikler edindim ki, hala hayret ediyorum.

Tahminimce 2010 yılında yazdığım, baktıkça bu güne dek ne kadar yol katettiğimi gördüğüm bir proje IndexMaker.

O zamandan bu zamana neler öğrenmişim, neleri yanlış yapmışım hepsini sizlerle paylaşabileceğim çok güzel bir case-study olacak.

Refactoring 101, bir yazı dizisi olsun isterim. Adım adım yaptığım iyileştirmeleri tek tek açıklayarak paylaşacağım.

Dizinin özeti, kabaca aşağıdaki gibi.

Use-case: Windows işletim sisteminin kullandığı dosya sistemi için bir gezgin yazmak istiyoruz. Bu uygulamayı yazdığım dönemde içerik arşivciliği çok yaygındı. Filmler, diziler, müzikler harddisklerde saklanır ve paylaşılırdı. Haliyle kendi içeriğini ve kimde hangi içeriklerin olduğunu listelemek ciddi bir problemdi. Bu probleme çözüm amaçlı böyle bir uygulama geliştirmişim zamanında.

O zamanlardan kalan blog postu da burada.

Neler göreceğiz:

  • SOLID Principles
  • Domain Driven Design
  • Clean Architecture
  • .Net Core Web API projesi geliştirme
  • .Net Core MVC projesi geliştirme
  • Aklıma gelmeyen ama refactor ederken değineceğim daha nice şirinlikler!

Bir yazılım geliştiricinin bilmesi gereken pek çok konuyu bu uygulama üzerinde paylaşmış olmayı umuyorum.

Sorular ve öneriler geldikçe, daha da gelişeceğinden şüphem yok.

2010 yılında yazdığım kodu da içeren Github repository’sine ait linke de buradan erişebilirsiniz.

Her release çıkardığımda yaptığım iyileştirmeleri detaylı olarak aktaran bir ReadMe dosyası ile birlikte yeni bir klasör altında solution’ı bu repodan paylaşıyor olacağım.

Güzel ve yararlı bir dizi olmasını dilerim.

Sıradaki yazı, Refactoring 101: Isolation

Nasıl hızlı prototipleme ile kendi web uygulamanızı hayata geçirebilirsiniz?

Eğer web tabanlı ya da online bileşenleri olan bir uygulama projeniz varsa, 3 dakikanızı ayırıp bu yazıyı okuyabilirsiniz.

Basit bir iş ve aksiyon planı ile projenize nasıl hızlı başlangıç yapabilirsiniz? Hem de cebinizden çıkacak parayı minimize ederek!

Buyrun başlayalım.

Bir küçük not. Projenizin fizibilite araştırması, pazar araştırması, market stratejisi, gelir modeli gibi “business related” konuları hallettiğinizi varsayıyorum. Başka bir yazıda da bunlara değiniriz. Şimdilik bu yazıda teknolojik açıdan projeyi sırtlayacağız.

Öncelikle teknolojik eğilimlerimizi belirleyeceğiz. Benim yönelimlerim aşağıdaki gibi…

Clean mimari ve DDD ile ilerleyeceğiz. Olabildiğince microservice mimarisini tercih edeceğiz ancak hızlanacağımızı düşündüğümüz yerde monolithic mimariye geçişten korkmayacağız.

Not: Çevik yazılım geliştirme metodolojisini benimseyeceğiz. Bu nedenle kafamızda proje için kuracağımız model “başla-bitir” değil “sadece yap-daha iyi yap-en iyisini yap” olacak. Kısıtlı zamanda olabildiğince fazla çıktı üretmeye odaklanacağız. Fazla çıktı üretmek demek bug’lı bir API ya da düzensiz bir veritabanı demek kesinlikle değil; olabileceğinin en iyisi olmayan ama görevlerini layığıyla yerine getiren yapılar demek.

Devam…

Microsoft teknolojileri ile ilerlemeyi tercih ediyorum. .Net Core 2.2 güzel bir seçim. Eğer RDB ihtiyacımız varsa MSSQL Server Express tercih edebiliriz. MySQL ya da PostgreSQL de güzel opsiyonlar. NoSQL DB ihtiyacımızı da MongoDB ile karşılayabiliriz. RethinkDB de bir zamanlar güzel bir opsiyondu. Onu da anmadan geçmeyelim…

Bize bir API gerekli. Böylelikle front-end ile back-end’i birbirinden tamamen izole çalıştırıp belki de front-end tarafında MS dışında teknolojiler ile ilerleme yolunu da açalım kendimize.

Görünüşe göre kurgulayacağımız clean mimari API, Infra, Domain ve bir Web projesinden oluşacak şimdilik. Yolda ihtiyaçlarımıza göre hamuru yoğurmaya devam edebiliriz.

Geliştirmeye başlamadan önce altyapıyı ve diğer ek gereksinimleri halledeceğiz. Sonrasında da güzel bir proje iş planı ile koda girişeceğiz.

  • Alan adı

Aciliyeti olmasa da ben genellikle bir projeye başlarken potansiyel alan adlarını alternatifleri ile kenara ayırmayı seviyorum. Son kullanıcının erişimine açılacak bir alan adı projenin lansmanına kadar aciliyeti olan bir gereksinim değil. Ancak güzel bir alan adı bulduğunuzda kaçırmamakta da fayda var.

Eğer ki proje kaynaklarına erişim amacıyla bir domain edinmekten bahsediyorsak burada ücretsiz çözümlerden olan dyn-dns ya da no-ip gibi araçlardan faydalanabilirsiniz. Örneğin kiraladığınız sanal makine üzerindeki test API’larına ve test Web uygulamasına kolay erişim için akılda kalıcı bir pseudo-domain adını bu araçlardan edinebilirsiniz.

  • Web uygulamasını barındıracağımız ortam

Web uygulamasının yalnızca front-end barındırdığını düşünürsek basitçe görevi son kullanıcının API ile konuşmasını sağlamak olacağa benziyor. Bu sebeple başlangıç için basit bir linux ya da windows hosting ile ilerleyebiliriz.

  • API uygulamasını barındıracağımız ortam

Bu bölüm diğerlerine nazaran daha kritik. .Net Core ile ilerliyor da olsak özellikle başlangıç için tam erişime sahip olduğumuz bir altyapıya sahip olmak bize hız kazandırabilir. Bu sebeple bir sanal sunucu ile ilerleyeceğiz. Bu prototipleme için AWS üzerinden bir t2.micro EC2 sanal sunucuyu 12 aylık ücretsiz olarak açabiliriz. 1 GB Ram’e sahip ve lisanslı Windows Server 2019 ile geliyor. CRUD operasyonları için ilk etapta yeterli olacaktır. Velev ki yetersiz kalmaya başladı, herhangi bir migration yapmadan ücretli modele geçip kaynakları yükseltmek çok kolay. Ki eğer t2.micro ile yetinemiyorsak, ücretli modeli karşılayacak paranın gelişi çok uzak değil demektir…

  • RDB ve/veya NoSQLDB bileşenlerini barındıracağımız ortam

Yine aws üzerinden hazır paketlenmiş ücretsiz linux mongoDB sanal makinesi açabilir ya da daha da hızlanmak istiyorsak API uygulamasını barındıracağımız sanal makineye mongoDB kurulumu gerçekleştirebiliriz. İkisini aynı makineye kurmak tüyler ürpertse de eğer bize prototipleme aşamasında hız kazandıracaksa ötesini düşünmeye gerek yok!

Kodlamadan önce altyapı gereksinimlerimizi tamamladık. Şimdi tercihen TFS, SVN ya da Git ile Azure DevOps Server, Github ya da Bitbucket gibi bir VCS eşliğinde prototip kodlamaya girişebiliriz.

Önemli Not: Böyle bam güm ilerlemenin bir getirisi olarak, her sprint sonunda çıkan release’i karşımıza alıp bunun neresini daha iyi yapabiliriz diye düşünmek zorundayız. Ve işin içine yalnızca kodlamayı değil, altyapı ve teknoloji tercihlerimizi de dahil edeceğiz. Böylelikle iş işten geçmeden bu proje ve bu projenin müşterisi için doğru tercihleri kolaylıkla görebileceğiz ve gerçekleştirebileceğiz.

Bir sonraki yazıda kodlamaya girişmeden önce yazılımın mimari tasarımını nasıl yaptığımızı, front-end için kullanıcı arayüzünü ve kullanıcı deneyimini iyileştirecek tercihleri nasıl belirlediğimizi tartışalım.

Github Madenciliği!

Bilgisayar mühendisliği lisans yıllarımda malesef ki çok aktif kullanamadığım, ama yıllar geçtikçe faydasını çok gördüğüm Github’ı kullanın!

Yeni nesil müstakbel meslektaşlarımdan görebildiğim, Github’ın hakkını vermiyorlar.

Tavsiyemdir, her ödevi daha ödeve başlamadan önce bir github’da aratın. Kodunu çalın diye demiyorum, derdim kıymetli vakti verimli kullanın.

Bugüne kadar yaptığım neredeyse tüm kafa yakan ödevler ya da tekerrür eden bir probleme bulduğum generic çözümleri fırsat buldukça atıyorum Github’a.

Bana github’daki bir kodu/kodumu atıf vererek soru sormaya gelen genç arkadaşları da alnından öpüyorum!

Güzel çalışın, iyi yetişin.

Siz bize lazımsınız…

Buraya da github linkimi bırakayım, hayrını görün: https://github.com/ycansener

C’de Pointer Aritmetiği – Sıralama Algoritmaları (Bubble Sort Algoritması)

Final dönemi nedeniyle verilen zorunlu ve uzun bir aradan sonra C’de pointer aritmetiği üzerine başlattığım yazı dizisine sıralama algoritmaları ile devam ediyorum.

Bu konuya ilişkin geçmiş paylaşımlardan yola çıkarak pointer aritmetiğinin mantığını genel olarak kavradığımızı umuyorum. Bu nedenle işin pointer kısmına doğrudan kod üzerinde değineceğim. Şimdi sıralama algoritması nedir, bunu inceleyelim..

Read more

C’de Pointer Aritmetiği – Linear Search Algoritması

Pointer aritmetiği ile yapılacak  işlemlere geçmeden önce, arama algoritmaları nasıl işler ne işe yarar ondan bahsedelim.. Dizi, yığıt, kuyruk gibi veri yapılarıyla işlem yapmaya başladığınızda veri yapısına ekleme-silme işlemleri dışında en çok kafa yorulacak konu arama ve sıralama algoritmalarıdır. Arama algoritmalarıyla başlıyoruz..

Bir veri yapısında (örneğimiz için kullanacağımız dizi gibi..) arama yapabilmek için öncelikle veri yapısı içerisinde elemandan elemana istediğimiz gibi dolaşabilmemiz gerekir. Zaten diziyi dolaştıktan sonra geriye yalnızca bir karşılaştırma işlemi eklemek kalıyor ki bu da oldukça basit bir işlem..

Özetle, bir arama algoritmasının yapması gereken işlemi, “gez – karşılaştır(kontrol et) – sonuç döndür” şeklinde özetleyebiliriz. Hangi arama algoritması olursa olsun bu üç işlemi yerine getirmek zorundadır.

Şimdi bu arama algoritmaları içinden, en kolay anlaşılanı “Linear Search” ile başlayalım..

Öncelikle etkinliği hakkında birkaç şey söyleyeyim. Bir algoritmanın etkinliği pek çok duruma göre değişkenlik gösterebilir. Örneğin çok fazla kayıt içeren bir veri yapısı üzerinde arama işlemi yapıyorsak bu algoritma oldukça masraflı olacaktır. Ancak sıralı olmayan ve makul sayıda kayıt içeren bir veri yapısı için, kullanılabilecek en etkin yöntemlerden biridir.

Peki ne yapıyor bu algoritma ?

Oldukça basit.. Aradığımız elemanı, veri yapısının ilk elemanından itibaren tüm elemanlarla karşılaştırmak üzere bir döngü başlatıyor. Eğer eleman bulunursa bulunduğuna dair bir sinyal yaratıp (örneğin “bulundu” isimli değişkenin değerini 1 yapmak gibi) döngüden çıkıyor. Şayet bulunamazsa (dolayısıyla sinyal yaratılmamışsa) ve döngü bitmiş ise, elemanın dizide bulunmadığını belirtecek başka bir sinyal yaratıyor. (“Sinyal = return değerindeki değişiklik” veya “Sinyal = değişken değerindeki değişiklik” olarak düşünülebilir. Ben sinyal demeyi tercih ettim. )

C ile yazılmış algoritmayı görelim..

<h1>include <stdio.h></h1>
<h1>include <stdlib.h></h1>
//@author yigitcansener

int linear_search_ile_arama_yap(int [],int); //pointer kullanmadan doğrudan diziyi alarak arama yapacak linear search fonksiyonu..
int main()
{
int dizi[] = {1,4,3,6,5,8,2,9,11,0}; //10 elemanli bir dizi tanimladik..
int aranacak_sayi;
int bulundu=0;
printf("Aranacak sayiyi giriniz: "); //Kullanıinidan aranacak sayi istendi..
scanf("%d",&aranacak_sayi); //Sayi alindi..

if((bulundu = linear_search_ile_arama_yap(dizi,aranacak_sayi)) == -1) //Eger arama sonucu olarak -1 donerse, listede aranan eleman olmadigi anlasiliyor…
printf("Aradiginiz sayi listede yok!\n");
else
printf("Aradiginiz sayi listede %d. elemanda bulundu.\n",bulundu); // Eger donen eleman -1 den farkli ise, elemanin listede bulundugu anlasiliyor ve elemanin listedeki konumu donduruluyor..

return 0;
}

int linear_search_ile_arama_yap(int dizi[],int aranan)
{
int i;

for(i=0;i<10;i++) // Dizi geziliyor..
if(dizi[i] == aranan) // Karsilastirma islemi yapiliyor..
return i; // Eger esitlik var ise esitligin bulundugu indeks return ediliyor.. (Sinyal)

return -1; // Eger dongu sonunda hic esitlik bulunmazsa, yani icteki return hic isletilmemisse, fonksiyondan -1 dondurulerek bir ust fonksiyona dizide elemanin bulunmadigi bildiriliyor..(Sinyal)
}

Kaynak koddan da anlaşılacağı gibi işlemler oldukça basit. Şimdi işin içine pointerları karıştırıp bir arama fonksiyonu daha oluşturalım. Büyük ölçüde bu koda bağlı kalarak, hatta bu kodu düzenleyerek elde etmeye çalışalım..

<h1>include <stdio.h></h1>
<h1>include <stdlib.h></h1>
//@author yigitcansener

int linear_search_ile_arama_yap(int *,int); //Diziye ait pointer ile arama yapacak linear search fonksiyonu..
int main()
{
int dizi[] = {1,4,3,6,5,8,2,9,11,0}; //10 elemanli bir dizi tanimladik..
int aranacak_sayi;
int *ptr_dizi; // Dizinin ilk elemanini gosterecek olan pointeri tanimladik..
int bulundu=0;
printf("Aranacak sayiyi giriniz: "); //Kullanıinidan aranacak sayi istendi..
scanf("%d",&aranacak_sayi); //Sayi alindi..

ptr_dizi = dizi; //Dizinin yalnizca adini kullanarak, baslangic adresini ptr_dizi pointerine aktardik.

if((bulundu = linear_search_ile_arama_yap(ptr_dizi,aranacak_sayi)) == -1) //Eger arama sonucu olarak -1 donerse, listede aranan eleman olmadigi anlasiliyor…
printf("Aradiginiz sayi listede yok!\n");
else
printf("Aradiginiz sayi listede %d. elemanda bulundu.\n",bulundu); // Eger donen eleman -1 den farkli ise, elemanin listede bulundugu anlasiliyor ve elemanin listedeki konumu donduruluyor..

return 0;
}

int linear_search_ile_arama_yap(int *ptr,int aranan) //Pointer aritmetigi kullanarak arama yapan fonksiyon..
{
int i;

for(i=0;i<10;i++) // Dizi geziliyor..
{
if(*ptr == aranan) // Karsilastirma islemi yapiliyor..
return i; // Eger esitlik var ise esitligin bulundugu indeks return ediliyor.. (Sinyal)

ptr++;//Dizinin bir sonraki elemanina gecilmesi icin pointer degiskeninin degeri arttiriliyor..
}
return -1; // Eger dongu sonunda hic esitlik bulunmazsa, yani icteki return hic isletilmemisse, fonksiyondan -1 dondurulerek bir ust fonksiyona dizide elemanin bulunmadigi bildiriliyor..(Sinyal)
}

Görüldüğü gibi birkaç küçük değişiklik ile fonksiyonu pointerlar üzerinden işler hale getirdik..

Bir sonraki yazıda, sıralama algoritmaları ve pointer aritmetiği kullanarak sıralama algoritması tasarlama üzerinde duracağız..

C’de Pointer Aritmetiği – Fonksiyona Pointer Geçirme

Fonksiyonel programlamaya uygun olarak tasarlanacak bir programda şüphesiz ki pointer’lar da fonksiyon trafiğinden nasibini alacaktır. Çok da mantığına inmeden basitçe bunu nasıl yapabileceğimizi göstereyim;

Örneğin bir sayacımız olsun.Amacımız da bu sayacı main fonksiyonu dışında bir fonksiyonda arttırıp, main’e geri döndürmek ve değerinin değişmesini sağlamak olsun.

Bu işlemi pointer kullanmadan, değişkeni global tanımlayarak da sağlayabilirsiniz.

<h1>include <stdio.h></h1>
<h1>include <stdlib.h></h1>
void deger_arttir(int *);
int main()
{
int i;

int sayac = 0;

int *ptr_sayac;

ptr_sayac = &sayac;//Pointer 'sayac' degiskeninin adresini gosteriyor…

//Sayac degiskeni ile islem yapilmadan, fonksiyona sayac degiskeninin pointeri gonderiliyor ve deger arttirma islemi yapiliyor…
for(i=0;i<10;i++)
{
deger_arttir(ptr_sayac);
printf("Sayacin yeni degeri: %d\n",sayac);
}
return 0;
}

void deger_arttir(int *degiskene_ait_pointer) //Disaridan integer deger tutan bir hucreyi isaret eden pointer alinip, degeri arttiriliyor…
{
*degiskene_ait_pointer += 1;
}