Çağdaş Kod Geliştirim Teknikleri


Bundan tam 16 yıl önce NASA, Mars Rover misyonunu gerçekleştirmek için Opportunity isimli Rover'ı zeminden ve kaya parçalarından model toplamak, su olup olmadığını araştırmak, kısacası gezegeni keşfetmek için Mars'a gönderdi. Kısa adıyla Oppy'nin planlanan görev süresi 3 aydı, fakat tam 8 Mars - 15 dünya yılı Mars yüzeyinde başarıyla görev yaptı. En nihayetinde 2019'un Şubat ayında dünyadan gönderilen kapatma sinyaliyle de görevine son verildi ve hizmetleri için tüm dünya tarafından taktir edildi.
Ve NASA'dan yeni bir misyon daha : MARS 2020...

Tıpkı Oppy örneğindeki gibi, dünya milletleri olarak; üstünden yıllarca zaman geçmesine rağmen çok eski yazılımsal ürünlerden hala faydalanıyor olabiliriz. Kısa vadeli planlarla kurgulanan yazılımlar, tahmin edilenden çok daha uzun süre bizlere hizmet vermiş olabilir. Fakat, onlarında artık vadelerinin dolmaya başladığının farkında olmanız, verdikleri hatalardan çığlıklarını duymanız ve çağdaş dünyanın algoritmik yapısına uygun yazılımlar türetmeniz gerekiyor. Özetle, üzerinde yıllar eskiterek ürettiğiniz kodlara kapatma sinyallerini gönderme vakti.

Yazının başında NASA'dan, Mars'dan, Oppy'den bahsettik ama sizin yapacaklarınız Mars'a rover göndermek kadar da zor değil.

Birkaç basit teknikle ürettiğiniz kodları daha anlaşılır daha kullanışlı ve daha performanslı hale getirebilirsiniz.

Nasıl mı?

  1. Değişkenleri mümkün oldukça veri tipleriyle birlikte tanımlayın, başlangıç değerleri aynı olan değişkenleri tek satırda = ile ayırarak yanyana yazın ve sonucunu bir değere eşitleyin, aynı iş grubunun alt elemanları olan değişkenleri gruplayın.

    Yazılım değişkenlerle başlar! Değişkenlerin değerleri kimi zaman algoritma içerisinde değişir, kimi zaman hiç değişmeden yoluna devam eder. Onlar değişsin ya da değişmesin, siz onları anlaşılır isimlerle ve mümkünse veri tiplerini belirterek tanımlayın. Her bir değişken ya da değişken grubunun, yapacağı işlemleri ifade edebilecek ve (tercihen) İngilizce isimlerden oluşmasını sağlayın. Aynı iş grubunda kullanacağınız değişkenleri bir struct grubu yapın. Dünya buna yapı dese de ben buna değişken paketleme diyorum :) Değişken paketleme işlemleri kod karmaşasını minimuma indirmenizi sağlayacak önemli araçlardan bir tanesidir.

    Örnek:
     
    <cfset rover = mars = structNew()>
    <cfset rover = {
        shortName : 'Oppy',
        fullName : 'Opportunity',
        taskPlace : 'Mars',
        missionStartYear : '2004',
        missionFinishYear : '2019'
    } />
    <cfset mars = {
        row : 4,
        area : "144.800.000",
        dayLength : '1 day 0 hour 37 min'
    } />
     
  2. Tek satırlık sonuçları olacak koşullar için üçlü ifade ( ternary statement ) halk arasındaki adıyla satır if kullanın, karmaşayı azaltın.

    Satır if olarak da adlandırdığımız üçlü ifade yöntemiyle; tek satırlık bir işlem için birden fazla satırdan oluşan koşullar yazmaya da kapatma sinyallerini gönderin. Onun yerine aşağıdaki gibi bir yöntem izleyin.

    <cfset roverName = structKeyExists( rover, "shortName" ) ? rover.shortName : rover.fullName />

    Yukarıdaki kodda rover struct'ı içerisinde shortName index'i mevcutsa roverName değişkenine rover.shortName değerinin, mevcut değilse de rover.fullName değerinin atanmasını sağladık.
     
  3. Array ve Struct lezzetlerinden faydalanın ( ArrayFilter, ArrayMap, ArrayReduce, StructFilter, StructMap, StructReduce )

    Diziler ve objeler yazılımın vazgeçilmezlerindendir.
    Veri modelleri oluşturmamıza, verileri gruplayabilmemize, bir bütün olarak ya da tek tek işleyebilmemize olanak sağlarlar.
    İçerdikleri verileri işleyebilmek için elbette çeşitli fonksiyonlara ihtiyaç duyulur. Bu fonksiyonlar sayesinde çok daha uzun ve karmaşık olabilecek işlemleri daha basit bir şekilde çözebiliyoruz.

    Örneğin elinizde gezengenlerin özelliklerini de içeren bir gezegen dizisi var. Siz bu dizi içerisindeki verileri bazı filtrelere göre listelemek ya da içerdiği bazı sayısal değerleri toplamak istiyorsunuz. Bakalım aşağıdaki örnekler istediğiniz gibi mi ?
     
    <cfset planetModel = ArrayNew(1) >
    <cfset planetModel = [
        { row : 1, name : 'Mercury', area : 74800000, dayLength : '58 day 15 hour 30 min' },
        { row : 2, name : 'Venus', area : 460200000, dayLength : '116 days 18 hours 0 min' },
        { row : 3, name : 'World', area : 510100000, dayLength : '24 hours' },
        { row : 4, name : 'Mars', area : 144800000, dayLength : '1 day 0 hour 37 min' }
    ] />
     
    Örnek 1: Gezegenlerden, yüzölçümü 300.000.000'dan büyük olanların çıktılanması.

    ArrayFilter
     
    <cfset planetFiltered = ArrayFilter( planetModel, function( item ){ return item.area > 300000000 } ) />
     
    ArrayFilter 1. elemanı dizi, 2. elamanı dönüş fonksiyonu olmak üzere 2 parametre alır. 1. parametrede vermiş olduğunuz dizinin her bir satırı için 2. parametrede fonksiyon olarak tanımladığınız işlemi uygular.
    ArrayFilter, filtreye göre ayıklama işlemlerinde oldukça başarılıdır, tüm dizi elemanlarına filtreyi sırasıyla uyguladıktan sonra sonucu yine bir dizi olarak döndürür.

    Meraklısına : https://cfdocs.org/arrayfilter  

    Artık elimizde yüzölçümü 300.000.000'dan büyük olan gezegenlerin dizisi var. Diyelim ki bu dizideki her bir gezegenin şuanki yüzölçümlerinin 2 katını merak ediyoruz? Gelin bunu da hesaplayalım.

    Örnek 2 : Yüzölçümü 300.000.000'dan büyük olan gezegenlerin şuanki yüz ölçümlerinin 2 katının yeni bir index olarak eklenmesi.

    ArrayMap

    <cfset planetMapped = ArrayMap( planetFiltered, function( item ){ item.areax = item.area * 2; return item } ) />

    ArrayMap 1. elemanı dizi, 2. elamanı dönüş fonksiyonu olmak üzere 2 parametre alır. 1. parametrede vermiş olduğunuz dizinin her bir satırı için 2. parametrede fonksiyon olarak tanımladığınız işlemi uygular.
    ArrayMap, dizi içerisindeki struct elemanlarından yeni bir sonuç üreterek her bir struct'a yeni bir eleman olarak atar. Sonucu bir dizi olarak döndürür.

    Meraklısına : https://cfdocs.org/arraymap

    Son olarak elimizde yüzölçümü 300.000.000'dan büyük olan gezegenlerin yüzölçümlerinin 2 katını da içeren bir dizi var ve biz de tüm gezegenlerin yüzölçümlerinin 2 katının toplanıp bize sonuç olarak dönülmesini istiyoruz. Bu örneği de şu şekilde kodlayabiliriz.

    Örnek 3 :  Yüzölçümü 300.000.000'dan büyük olan gezegenlerin şuanki yüz ölçümlerinin 2 katının toplanarak sonucunun çıktılanması.

    ArrayReduce

    <cfset planetReduced = ArrayReduce( planetMapped, function( acc, item ){ acc = acc? : 0; acc += item.areax; return acc; } ) />

    ArrayReduce 1. elemanı dizi, 2. elamanı dönüş fonksiyonu olmak üzere 2 parametre alır. 1. parametrede vermiş olduğunuz dizinin her bir satırı için 2. parametrede fonksiyon olarak tanımladığınız işlemi uygular ve tek bir sabit sonuç üretir. 

    Meraklısına : https://cfdocs.org/arrayreduce

    Bu fonksiyonları içi içe pipe denilen yöntemle kullanabilirsiniz. Onu da şu şekilde gösterelim.

    <cfset result = ArrayReduce( ArrayMap( ArrayFilter( planetModel, function( item ){ return item.area > 300000000 } ), function( item ){ item.areax = item.area * 2; return item } ), function( acc, item ){ acc = acc? : 0; acc += item.areax; return acc; } ) />

    Bu 3 fonksiyonu da detaylıca anlatan, sevgili Halit Yurtaş'ın makalesine bağlantıdan ulaşabilirsiniz : https://wiki.workcube.com/help/9814
     
  4. İşlemlerinizi parçacıklara ayırın, fonksiyonelliğe dikkat edin, parçaları kullanarak bütünü oluşturun.

    Diyelim ki sıfırdan bir geliştirim yapıyorsunuz. Yaptığınız bu geliştirimde önceliğiniz; işinizi görebileceğini düşündüğünüz, hali hazırda yazılmış fonksiyonları kontrol etmek ve kendi geliştiriminizde kullanışlılığını denetlemek, mümkünse kullanmak olmalı.
    İhtiyacınızı görebilecek bazı fonksiyonlara ulaştığınızda, onlarla bütünleşik çalışabilecek bir algoritma kurgulayın. Eksikleri varsa parametrik geliştirimlerle eksiklerini kapatın. Harici olarak yapacağınız geliştirimleri fonksiyonel olarak düşünün. Daha sonradan farklı geliştirimlerde de aynı ihtiyaçların olabileceği ihtimalini unutmayın. Kod karmaşından kurtulmak, hata yönetimini kolaylaştırmak ve performansı artırmak için fonksiyonellik vazgeçilmez bir kavramdır.

    İşi parçacıklara ayırmanın en uygun yöntemi component oluşturmak ve bu component içerisinde fonksiyonlara ayırmaktır.

    Component oluştururken aşağıdaki durumlara dikkat edin!

    Oluşturduğunuz componentlerin construct ( kurucu - init - initialize ) fonksiyonlar içermesine özen gösterin.
    Kapsülleme işlemlerini ihmal etmeyin ( getter ve setter )
    Fonksiyon ve değişken tiplerini tanımlayın, propertylerle çalışın.
    Scope yönetimine dikkat edin, kendinizi this ve super scopelarını kullanmaktan alıkoymayın.
    Fonksiyonların varsa döndüreceği değer tiplerini ve erişilebilirlik özelliklerini tanımlayın.

    Aşağıda basit bir component oluşturarak, kullanımını örnekledim.

    Örnek : Tüm gezegenlerin alt özellikleriyle birlikte gruplanması ve gönderilen gezenen ismine göre ilgili gezegenin tüm alt özellikleriyle birlikte listelenmesi.
     
    <cfcomponent>
        <cfproperty name = "planetModel" access="public" type = "Array" required = "true" />
        <cffunction name = "init" access = "public">
            <cfargument name = "planetModel" type = "Array" required = "false" />
            <cfset this.planetModel = ( isArray( arguments.planetModel ) and ArrayLen( arguments.planetModel ) ) ? arguments.planetModel : [] />
            <cfreturn this />
        </cffunction>
        <cffunction name = "setPlanet" access = "public" hint = "Add new item to array">
            <cfargument name = "planet" type = "struct" required = "true" />
            <cfset ArrayAppend( this.planetModel, arguments.planet ) />
        </cffunction>
        <cffunction name = "getPlanet" access = "public" returnType = "struct" hint = "Add new item to array">
            <cfargument name = "planetIndex" type = "numeric" required = "true" />
            <cfreturn this.planetModel[arguments.planetIndex] />
        </cffunction>
        <cffunction name = "getPlanetByName" access = "public" returnType = "Struct" hint = "Get item by planet name">
            <cfargument name = "planetname" type = "string" required = "true" />
            <cfset planetname = arguments.planetname />
            <cfreturn ArrayFilter( this.planetModel, function(item){ return item.name == planetname } )[1] />
        </cffunction>
    </cfcomponent>
     
    <cfset planetModel = ArrayNew(1) >
    <cfset planetModel = [
        { row : 1, name : 'Mercury', area : 74800000, dayLength : '58 day 15 hour 30 minutes' },
        { row : 2, name : 'Venus', area : 460200000, dayLength : '116 days 18 hours 0 minutes' },
        { row : 3, name : 'World', area : 510100000, dayLength : '24 hours' },
        { row : 4, name : 'Mars', area : 144800000, dayLength : '1 day 0 hour 37 minutes' }
    ] />
    <cfset planetComponent = createObject( "component", "planet" ).init( planetModel ) />
    <cfset planetComponent.setPlanet( planet : { row : 5, name : 'Jupiter', area : 6142000000, dayLength : '9 hours 56 minutes' } ) />
    <cfset getPlanet = planetComponent.getPlanet( planetIndex : 0 ) />
    <cfset getPlanetByName = planetComponent.getPlanetByName( planetIndex : 'World' ) />

    Yukarıdaki örnekte öncelikle planet.cfc isminde bir dosya yarattık ve içerisinde bir component oluşturduk.
    Componentin içerisinde planetModel isimli bir property ve init isminde bir kurucu fonksiyon tanımladık. Property'nin init fonksiyonu ile doldurulmasını sağladık.
    setPlanet fonksiyonuyla planetModel property içerisine yeni bir eleman atanabilmesini sağladık. (Setter)
    getPlanet fonksiyonuyla planetModel property içerisinden, gönderilen index'e göre ilgili gezegenin bilgilerinin döndürülmesini sağladık. (Getter)
    getPlanetByName fonksiyonuyla planetModel property içerisinden, gönderilen gezegen ismine göre gezegenin bilgilerinin döndürülmesini sağladık. (Getter)
     
    Yukarıda tarifini yapmaya çalıştığım çağdaşlaştırma çalışmalarının dışında farklı teknikler de var ama başlangıçta bu yöntemler bile uygulandığında kodunuzun ve zihninizin ne kadar da berraklaştığına tanıklık edeceksiniz!

    Esen kalın...

    Uğur Hamurpet

Geri Bildirim

Bu içeriği faydalı buldunuz mu?