Quantcast
Channel: Burak Selim Şenyurt
Viewing all 351 articles
Browse latest View live

Tek Fotoluk İpucu 152 - DebuggerDisplay Niteliği ile Debugging Daha Sevimli Olabilir

$
0
0

Merhaba Arkadaşlar,

Nitelikler bildiğiniz üzere çalışma zamanına bilgi taşımak amacıyla kullanılan önemli tiplerdendir. Visual Studio tarafında da işimize yarayan bir çok nitelik(Attribute) yer alır. Bunlardan birisi DebuggerDisplay'dir. Önce aşağıdaki ekran görüntüsündeki kod parçasını göz önüne alalım.

Product isimli sınıfımız ve kendisinden örneklenen nesnelere sahip bir listemiz var. Debug modda olduğumuz kesin. basket değişkeni üzerinde durduğumuzda ise UseDebuggerDisplay.Product şeklinde Namespace.typeName notasyonuna uygun çıktılar görüyoruz. Aslında bir şeyleri debug ederken bu tip listeler üzerinde işe yarar bilgilerin görünmesi daha iyi olabilir(Nitekim + ile açıp ulaşmaya çalışmak yerine o anda hemen görebilmek benim tercih ettiğim bir görüntülenme şekli) Visual Studio bir çalışma zamanı ortamı olduğuna göre ona bunu öğretmemiz gerekiyor. İşte DebuggerDisplay niteliği bu aşamada devreye giriyor. Nasıl mı? Aynen aşağıdaki fotoğrafta görüldüğü gibi.

Tek yaptığımız Product sınıfının başına System.Diagnostics alanında bulunan DebuggerDisplay niteliğini uygulamak oldu. {} içerisinde verilen DebugMessage ifadesinin Product sınıfının private bir özelliği olduğuna ve yanlızca okunabilir tanımlandığına(mecburi değil) dikkat edelim. Read-Only olması mantıklı çünkü bu özelliği object user değil Visual Studio ortamı kullanıyor. Aslında DebugMessage özelliğinin döndürdüğü string içeriğin formatını DebuggerDisplay niteliğinde doğrudan kullanabiliriz de ancak bu şekilde gerekli ifadeyi(expression), nitelikten alıp bir fonskiyonda sarmalamış oluyoruz. get bir metod bloğu olduğundan içerisinde çok daha farklı işlemler yapabiliriz. Söz gelimi bu bir kategori tipi olsa ve içerisinde ürün listesi barındırsa, kategori adının yanında kaç tane ürün olduğunu ve toplam liste fiyatını göstermek için gerekli hesaplamaları bu blok içerisinde yazabiliriz(Bence bunu bir deneyin) 

Aynı işi aslında ToString metodunu override ederek yapma şansına da sahibiz. Diğer yandan DebuggerDisplay niteliği üzerinden çalışma zamanında bazı özel işvelsellikle gönderme şansına da sahibiz. Söz gelimi nq=No Quatos şeklinde bir bilgi gönderebiliriz. Bir diğer tercih sebebi de az önce bahsettiğimiz gibi ToString'in private bir metod olmayışıdır. Tipin ToString metodunu dışarıya açmak istemediğimiz durumlarda DebuggerDisplay kullanımı ve bu sorumluluğun private bir metoda devredilmesi mantıklıdır.

Bir başka ipucunda görüşmek dileğiyle hepinize mutlu günler dilerim.


GoLang - Basit HTTP Web Server Yapımı

$
0
0

Merhaba Arkadaşlar,

Go dili ile ilgili maceralarım devam ediyor. Dilin temel özelliklerini anlamaya çalışmak bir yana, aralarda merak ettiğim farklı konuları da incelemeye çalışıyorum. Uygulamalı örnekler üzerinden gitmek de bir programlama dilini öğrenirken tercih ettiğim yollardan birisi. Size de tavsiye ederim.

Geçtiğimiz günlerde REST(Representational State Transfer) servislerinin nasıl yazıldığına bakarken bir kaç yeni şey daha öğrendim. Amacım HTTP Get metodu ile basit REST servis talebi yapmak ve örneğin bir ürün listesini JSON(JavaScript Object Notation) formatında istemciye döndürmekti (Daha önceden Ruby ve Python'da REST servislerin nasıl yazılabileceğine de bakmıştım) Go tarafındaki durumu araştırırken HTTP olarak gelecek talepleri nasıl karşılayabileceğimi de gördüm.

Dilerseniz vakit kaybetmeden örneğimize ait kodlara geçelim. Programımız wsrv.go isimli bir dosyadan oluşmakta. İçerisinde ürünler için bir struct ve diğer işlemler için gerekli temel fonksiyonları bulunacak. 

Uygulama Kodu

wsrv.go dosya içeriği;

package main

import (
    "fmt"
    "log"
    "net/http"
	"time"
	"strings"
	"encoding/json"
)

var products [4]Product
	
func homePage(writer http.ResponseWriter, request *http.Request){
    fmt.Printf("[%s]\t%s:%s\n",request.Method,time.Now(),request.URL)
	//gelen istege karsilik dosya varsa gosterir. yoksa http 404 verir
	http.ServeFile(writer, request, request.URL.Path[1:]) 
}

func getProduct(writer http.ResponseWriter, request *http.Request) {
	fmt.Printf("[%s]\t%s:%s\n",request.Method,time.Now(),request.URL)
	var url string=request.URL.Path
	var result []Product
	parts:=strings.Split(url,"/")
	
	for _, p := range products {
		if(strings.EqualFold(p.Category,parts[2])) {
			result=append(result,p)
		}
	}
	if len(result)==0{
		http.Error(writer, http.StatusText(404), 404)
	}else{
		json.NewEncoder(writer).Encode(result)
	}
}

func getProducts(writer http.ResponseWriter, request *http.Request){ 	
    fmt.Printf("[%s]\t%s:%s\n",request.Method,time.Now(),request.URL)
    json.NewEncoder(writer).Encode(products)
}

func main() {
	products[0]=Product{Id:9001,Title: "intel core i5", Category: "CPU", UnitPrice: 150.90}
	products[1]=Product{Id:9021,Title: "intel core i7", Category: "CPU", UnitPrice: 200.35}
	products[2]=Product{Id:7800,Title: "google mouse", Category: "OEM", UnitPrice: 44.35}
	products[3]=Product{Id:1450,Title: "Logitech Wireless Keyboard", Category: "OEM", UnitPrice: 18.98}
	
	http.HandleFunc("/", homePage)
	http.HandleFunc("/products/", getProduct)
	http.HandleFunc("/products", getProducts)
	log.Fatal(http.ListenAndServe(":8084", nil))
}

type Product struct {
    Id int "json:\"ID\""
	Title string "json:\"Title\""
	Category string "json:\"Category\""
    UnitPrice float32 "json:\"UnitPrice\""
}

Örnekte Home.html isimli bir giriş sayfası da kullanıyoruz. İçeriği aşağıdaki gibi.

<html><html><head><title>AZON Tools and Products</title></head><body><b>All Tools</b><br/><i>Whatever you want...We ara solution</i><br/><a href="http://localhost:8084/products">All Products</a></body></html>

Peki Neler Oluyor?

Tahmin edeceğiniz üzere main fonksiyonu içerisinde bazı yönlendirmeler mevcut. HTTP ile gelen Get taleplerini dinliyor ve basit bir route sistemi kullanıyoruz. Gerekli paketler import ile bildirilmiş durumda.

PaketKullanım Amacı
encoding/jsonÇıktıları JSON formatında verebilmek için gerekli fonksiyonellikleri içerir.
httpEn kilit paketimiz. HTTP taleplerini dinlemek ve çıktı üretmek için gerekli operasyonları içerir.
stringsproducts/OEM şeklindeki kategori bazlı ürünleri çekmek için gelen URL bilgisini / işaretine göre ayrıştırmaya çalışıyoruz. Burada kullandığımız Split fonksiyonu Strings paketinde yer alıyor. Tabii strings paketinde bir sürü ama bir sürü işe yarar fonksiyon var. İnceleyin.
logLog basmak için kullandığımız paket. Filmimizde oldukça küçük bir role sahip.
fmtGelen talebin ne olduğu, hangi zamanda yapıldığı ve HTTP Metodunun adını ekrana basarken standart Printf gibi fonksiyonlara başvuruyoruz. Bu fonksiyonu içeren paket.
timeİşlem zamanını yakalamak için kullandığımız Now fonksiyonunu içeren paket.


main fonksiyonu içerisinde kobay dizimiz olan products' a bir kaç Product nesne örneği ekliyoruz. Product tipi bir Struct. Sembolik olarak ürünün benzersiz numarasını(Id), adını(Title), liste fiyatını(UnitPrice) ve bulunduğu kategori(Category) bilgisini içeren değerler taşımakta. main içerisindeki diğer satırlar ise yazımızın kilit noktasını oluşturuyor. HandleFunc iki parametre almakta. İlki ele alınacak talebe ait adres bilgisi. İkincisi ise bu tip bir talep geldiğinde çalıştırılacak olan fonksiyon. Örneğin / için homePage, /products/ için getProduct, /products için getProducts fonksiyonları çağırılacak.

HandleFunc yönlendirmelerinin yapıldığı fonksiyonların ortak özelliği geriye değer döndürmeyip iki parametre almaları(ki bu bir tesadüf değil). İlk parametre ResponseWriter ikinci parametre ise Request tipi için bir Pointer olmalı. Bu fonksiyonlar içerisinde ResponseWriter örneğini kullanarak HTTP talebine cevap olacak çıktıları üretiyoruz. *http.Request işaretçisi üzerinden talep ile ilgili bir çok bilgiye ulaşabilmekteyiz. HTTP metodu ve URL bilgisi gibi. HandleFunc http paketi içerisinde aşağıdaki şekilde tanımlanmış bir fonksiyondur.

func HandleFunc(pattern string, handler func(ResponseWriter, *Request))

İlk parametrede deseni veriyoruz. Örneğimizde REST adresleri gibi düşündük. İkinci parametrede bir fonksiyon ataması söz konusu. Hatırlayacağınız gibi Go dilinde fonksiyonları , fonksiyonlara parametre olarak geçirebilmemiz mümkün. handler'ın tanımı ise paketle ilgili dokümana göre aşağıdaki gibi.

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request)

Go dilinin C# veya Java tarafında kodlama yapan birisine enteresan gelebilecek pek çok yanı var. Bu nedenle paketlerin içeriklerine bakmanızda ve özellikle fonksiyon tanımlamalarını incelemenizde yarar var.

homePage fonksiyonunun çalışma prensibi oldukça basit. İstemci tarafına üçüncü parametre ile gelen içeriği sunmakla görevli. Yani index.html isimli bir talep gelirse ve bu sunucunun dinleme yaptığı bir klasörde mevcutsa, istemci tarafına parse ediliyor. Çıktıyı ServerFile fonksiyonu gerçekleştirmekte. Elbette olmayan bir dosya talebi de gelebilir. Bu durumda HTTP 404 fırlatılır.

getProducts fonksiyonu nispeten daha basit. Tek yaptığı products dizisinin içeriğini JSON formatında sunmak. NewEncoder ve Encode fonksiyonlarını bu iş için kullanıyoruz. Product tipine ait üyelerin JSON çıktısında nasıl isimlendirileceği de yapı tanımı içerisinde yer alıyor. Burada JSON çıktısı içerisindeki alan adlarını değiştirerek kullanmamız mümkün. Bazen domain içerisinde kullanılan niteliklerin, servis olarak sunulduğu kaynaklara farklı isimlerde gösterilmesi tercih edilebilir.

getProduct fonksiyonu temel olarak belli bir kategorideki ürünlerin çıktısını JSON formatında vermek üzere tasarlandı. Çok ilkel bir yapısı var. Gelen adresteki kategori adını basit string ayrıştırma işlemleri ile yakalamaya çalışıyoruz. Tüm ürün listesini dolaşırken talep edilen kategori altında olanları ise yeni bir dizide topluyoruz. Son olarak bu diziyi JSON formatında sunuyoruz(Bir gerçek hayat örneğinde REST Api geliştirmek için tercih edilebilecek Go paketlerini kullanmanızı öneririm)

Bu arada main fonksiyonunun son satırında yer alan ListenAndServe çağrımı kodun çalıştığı makinenin 8084 adresi üzerinden HTTP dinlemesi yapılacağını belirtmekte. Örnekte http://localhost:8084 şeklinde bir adresin dinlenmesi söz konusu. İstediğiniz serbest bir Portu tercih edebilirsiniz tabii ki. Hatta bu tip n sayıda uygulamayı farklı portlar üzerinden hizmet verecek şekilde kullanıma açabilirsiniz de. N sayıda microservice'in kendi alanları ile ilgili izole edilmiş halde çalıştığını düşünün.

Çalışma Zamanı Testleri

Sırada testlerimiz var. Öncelikle uygulamamızı çalıştırmamız gerekiyor. Program 8084 adresine gelecek tüm talepleri Console penceresine de loglamakta. Bu mekanizmayı daha da geliştirebiliriz. Biz şimdilik HTTP metodunun tipini, talep edilen adresi ve zamanı bastık. Şimdi tek tek denemeler yapalım.

http://localhost:8084/home.html

Görüldüğü gibi tasarladığımız HTML sayfasını elde ettik. Burada çok daha şık bir sayfa sunulabilir. CSS kullanarak içeriği zenginleştirebilirsiniz. Biz basit bir HTML içeriğinin görüntülenebileceğini ifade ettik. All Products linki bizi ürün listesini alabileceğimiz sayfaya yönlendirecektir.

http://localhost:8084/products

Bu sefer tüm ürün listesini elde ettik. Hem deeee JSON formatındaaaa. Peki ya belli bir kategori altındaki ürünleri nasıl yakalayabiliriz? Örneğin OEM grubundaki ürünleri almak istersek aşağıdaki gibi bir talepte bulunmamız gerekir.

http://localhost:8084/products/oem

Bu sefer de OEM kategorisindeki ürün listesini çektiğimizi görebilirsiniz. Tabii olmayan bir kategori girilirse istemci tarafına HTTP 404 fırlatmayı da ihmal etmedik. getProduct fonksiyonu içerisinde ilgili kategoriye bağlı ürün/ürünler yoksa Error fonksiyonundan yararlanrak 404 Not Found durumunu fırlatıyoruz. Bu nedenle aşağıdaki gibi bir talebin sonucu 404 olacaktır.

http://localhost:8084/products/yokki

Yaptığımız tüm işlemleri sunucu uygulamasına ait console penceresinde izleyebiliriz. Basit detaylar koyduğumuzu fark etmişsinizdir. HTTP metodu, çağrı zamanı ve çağrı yapılan adres bilgisi. Çok daha fazla detay sunulabilir tabii. Geriye dönecek olan cevap, HTTP durum bilgisi, talep yapan istemciye ait bir takım bilgiler(IP gibi) yakalanabilir. Hatta log'lar bir araç ile farklı bir kaynağa da atılabilir. Örneğin bir dosyaya yazdırabiliriz. Bu yazımıza konu olan örnek içinse aşağıdaki çıktılar yeterli görünüyor.

Eksikler

Bu örnekte http paketini basit bir uygulama üzerinden az da olsa tanımış olduk. Ancak routing sistemi çok da kabiliyetli değil. Söz gelimi HandleFunc içerisinde {category} gibi bir yer tutucu kullanamıyor ve bunu ilgili fonksiyon içerisinde kolay bir şekilde ele alamıyoruz. Yani MVC tarafından aşina olduğumuz products/{category} gibi bir bildirimi yapabilmek güzel olurdu. Kullandığımız / işaretine göre ayrıştırma tekniği oldukça riskli ve ilkel. Dolayısıyla ya bir router yazmalıyız ya da hazır olan açık kaynaklardan birisini kullanmalıyız.

Diğer yandan örneğimizde HTTP'nin Post, Put ve Delete gibi diğer metodlarını ele almadık. Örneğin ürün listesine yeni bir ürünü nasıl ekleyebiliriz veya silebiliriz bunu keşfetmemiz gerekiyor. Veri deposu olarak kullandığımız ürün listesi için tercih ettiğimiz dizi de iyi bir seçim değil. Bunun yerine MySQL, Oracle, MongoDB, File veya daha farklı bir sistem tercih edebiliriz. Bu konuların araştırmasını siz değerli okurlarıma bırakıyorum. Eğer fırsatım olursa ben de bu konulara bakacağım zaten. Böylece geldik bir makalemizin daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

AREL Üniversitesi Bilişim, Teknoloji ve Endüstri 4.0 Etkinliği

$
0
0

Merhaba Arkadaşlar,

Kısmet olursa 7 Mart 2017 günü AREL Üniversitesi Bilgisayar Mühendisliği Kulübü tarafından düzenlenen "Bilişim, Teknoloji ve Endüstri 4.0" isimli etkinlikte Ruby programlama dili ile ilgili bilgilerimi aktarmaya çalışacağım. Dolu geçecek günün programında ilgi çekici harika konular ve konuşmacılar yer alıyor. İşte program.

Görüşmek dileğiyle, hepinize mutlu günler dilerim.

GoLang - Kalıtımsız Bir Dünya

$
0
0

Merhaba Arkadaşlar,

Go hızlı ve performanslı bir programlama dili olarak anılıyor. Diğer yandan nesne yönelimli dil özelliklerini büyük ölçüde içermediği gibi bir gerçek de var ortada. Tasarımı sırasında kalıtım(Inheritance) gibi yönetimin sonradan zorlaşabildiği ve bakım gerektiren çatıların performansı olumsuz yönde etkilediği düşüncesi hakim olmuş. Bu nedenle nesne yönelimli düşünce tarzını az da olsa kenara bırakarak ilerlemek gerekiyor. Kalıtım doğrudan desteklenmese de nesne kompozisyonu(Composition) mevcut. Hatta belli bir ölçüde çok biçimliliği(Polymorphism) de uygulayabiliriz gibi. 

Doğruyu söylemek gerekirse yıllarca nesne yönelimli dillerle çalışmış birisinin tekrardan okula dönüp alışık olduğu alan yapısı ve kurallar dizisinin dışına çıkması pek kolay değil. Bunu sancısını Go dilini öğrenmeye çalıştığım şu günlerde yoğun olarak yaşamaktayım. Gelin kalıtımı ve çok biçimliliği tam olarak karşılamasa da Go dilinde benzer kurguların nasıl yapılabileceğine bir bakalım. İşe aşağıdaki örnek kod parçasını geliştirerek başlayabiliriz.

Composition 

package main

// Inheritance ve Composition'a baslangic kodu
import (
	"fmt"
	)

func main(){
	buentap:=new(Gorlog)
	buentap.nick="buentap"
	buentap.level=4857
	buentap.color="red"
	writeGorlog(buentap)

	zulu:=new(Molag)
	zulu.nick="zulurak"
	zulu.level=3450
	writeMolag(zulu)
}

type Player struct{
	nick string
	level int
}

// Gorlog'lar insan irkindan gelir. Ten renkleri vardir.
type Gorlog struct{
	Player
	color string
}

func writeGorlog(g *Gorlog){
	fmt.Printf("%s - %d('%s')\n",g.nick,g.level,g.color)
}

// Molag'lar renksiz ruhlardir
type Molag struct{
	Player
}

func writeMolag(m *Molag){
	fmt.Printf("%s - %d\n",m.nick,m.level)
}

Kodun çalışma zamanı çıktısı aşağıdaki gibi olacaktır. 

Şuraya da çakma bir Object Composition çizelgesi koyalım. Player tipi esas itibariyle Gorlog ve Molag'ın birer parçasıdır. Nesneleri bu şekilde birleştirdiğimizi düşünebiliriz.

Kod içerisinde üç structure tanımlandığını görmektesiniz. Player tipinde nick ve level isimli üyeler mevcut. Diğer yandan Gorlog ve Molag olarak adlandırdığımız orta dünya tipleri içerisinde Player isimli bir tanımlama yer alıyor. Bir başka deyişle bu tipler aslında birer Player olarak düşünülebilirler. Nitekim bir Player'ın üyelerini barındıracak şekilde tasarlandılar. Farklılık olması açısından insan ırkından gelen Gorlog'ların renkleri de var(color isimli üye)

main fonksiyonunda üretilen nesne örneklerinin üretim şekilleri haricinde noktadan sonra Player yapısında gelen üyelerine değerler atadığımıza dikkat edelim. Yani bir Gorlog nesne örneği veya Molag nesne örneği üzerinden Player üyelerine erişebiliyoruz. Alın size kalıtımsal bir yaklaşım. Her tür aslında birer Player olarak düşünülebilir diyebilir miyiz?

Her iki tür için ekrana bilgi yazan birer fonksiyon da mevcut. writeGorlog, Gorlog tipinden, writeMolag ise Molag tipinden bir pointer değişkenini parametre olarak kullanıyor. Böylece ilgili fonksiyonlardan Player yapısında o nesne örnekleri için tanımlı nick ve level değerlerine ulaşabiliyoruz.

Interface Kullanımı

Gelelim bir diğer noktaya. Diyelim ki Gorlog ve Molag türleri için ortak bir takım işlevesellikler söz konusu. Örneğin hareket ve ateş etme kabiliyetleri olduğunu varsayalım. Normal şartlarda nesne yönelimli bir dil ile geliştirme yapıyor olsak kuvvetle muhtemel bir interface tanımlar ve ilgili tipleri bu interface tipinden türeterek üyeleri ezmeye zorlardık. Ancak bu kadar nesne odaklı bir dünyada değiliz. Yine de elimizde bir takım çözümler var. İlk olarak Go dilinde de interface tipi olduğunu belirtelim. Bu tip içerisinde tanımlanacak fonksiyonları, uygulamasını istediğimiz yapılar için yazabilir ve az da olsa çok biçimlilik sunan fonksiyonellikler sağlayabiliriz. Yukarıdaki örneğimize aşağıdaki değişikliler ile devam edelim.

package main
// Inheritance ve Composition'a baslangic kodu
import (
	"fmt"
	)
func main(){
	buentap:=new(Gorlog)
	buentap.nick="buentap"
	buentap.level=4857
	buentap.color="red"	
	writeGorlog(buentap)
	zulu:=new(Molag)
	zulu.nick="zulurak"
	zulu.level=3450
	writeMolag(zulu)
	moveAndFire(buentap,"korusant","tatuyin")
	moveAndFire(zulu,"tatuyin","korusant")
}
func moveAndFire(p IPlayer,moveL string,fireL string){
	p.move(moveL)
	p.fire(fireL)
}
type IPlayer interface{
	move(location string) bool
	fire(location string) bool
}

type Player struct{
	nick string
	level int
}

// Gorlog'lar insan irkindan gelir. Ten renkleri vardir.
type Gorlog struct{
	Player	
	color string
}
func (g Gorlog) move(location string) bool{
	fmt.Printf("%s:Move to %s\n",g.nick,location)
	return true
}
func (g Gorlog) fire(location string) bool{
	fmt.Printf("%s:Fire to the %s\n",g.nick,location)
	return true
}
func writeGorlog(g *Gorlog){
	fmt.Printf("%s - %d('%s')\n",g.nick,g.level,g.color)
}

// Molag'lar renksiz ruhlardir
type Molag struct{
	Player		
}
func (m Molag) move(location string) bool{
	fmt.Printf("%s:Move to %s\n",m.nick,location)
	return true
}
func (m Molag) fire(location string) bool{
	fmt.Printf("%s:Fire to the %s\n",m.nick,location)
	return true
}
func writeMolag(m *Molag){
	fmt.Printf("%s - %d\n",m.nick,m.level)
}

Eski alışkanlık olsa gerek IPlayer şeklinde isimlendirdiğimiz bir interface tipi mevcut. Bu tip içerisinde move ve fire isimli iki fonksiyon yer alıyor. Biz bu fonksiyonellikleri hangi yapılara kazandırmak istersek onlara uygulamalıyız. Uygulama şekli aslında aynı isimli fonksiyonları uygulanacak tip için yazmak. move ve fire fonksiyonlarının tanımlanış şekillerinde ilk parantezler arasında uygulanacağı yapıyı belirtiyoruz. Böylece ilgili nesne örneklerinin Player içerisinde tanımlı üylelerine ve kendi özel niteliklerine erişebiliriz. Fonksiyonlar için önemli olan nokta girdi ve çıktı için kullanılan parametre yapılarının IPlayer arayüzünde belirtildiği şekilde olması. 

Peki nerede nesne yönelimliye yakınlaşıyoruz? Go'nun kod yazım kuralları haricinde dikkat etmemiz gereken nokta MoveAndFire isimli fonksiyon. İlk parametre IPlayer tipinden(Bir ışık yandı mı sevgili C#çı, Javacı arkadaşım) Gorlog ve Molag isimli yapılar için tanımlanan move ve fire fonksiyonları, IPlayer tarafından tanımlanan şablona uygun olacak şekilde yazıldılar. Dolayısıyla IPlayer'a atanan tipin içerisinde bu fonksiyonların aynı desende yazılmış olması(tabii ilk parantez içlerine dikkat) yeterli. main fonksiyonunda yaptığımız moveAndFire çağırımlarında bu fonksiyona hangi nesne örneğini gönderdiysek o nesne örneğine ait fire ve move fonksiyonlarının çalıştırılması söz konusu.

Görüldüğü gibi Composition kuramını ve interface tipini kullanarak belli ölçüde nesne yönelimlilik sağladık. moveAndFire fonksiyonunun bir çeşit çok biçimlilik sağladığını da gördük. Bakalım Go dili ile ilgili olarak ilerleyen zamanlarda daha neler neler göreceğiz? Yazıyı hazırlarken goder isimli blog'dan oldukça faydalandığımı da ifade etmek isterim. Ufkumu açan çok değerli bir Türkçe kaynak. Gopher olmak isteyenlerin takip etmesini şiddetle tavsiye derim. Böylece geldik bir yazımızın daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

Tek Fotolu İpucu 153 - Config Sekmesini Harici Dosyadan Okumak

$
0
0

Merhaba Arkadaşlar,

Uygulamalarımıza ait pek çok parametrik bilgiyi zaman zaman config uzantılı dosyalar içerisinde tuttuğumuz çok olmuştur. Web tabanlı uygulamalar ve servislerde web.config, exe tipi uygulamalarda ise app.config dosyaları söz konusudur. Bu dosyalarda standard olarak kullanılan içerikler mevcuttur. appSettings, connectionStrings sanıyorum ki en popüler olanlarındandır. Peki bu tip konfigurasyon segmentlerinin harici dosyalardan da alınabileceğini biliyor muydunuz? Örneğin uygulamanın appSettings içeriğinin farklı bir dosyadan gelmesini sağlayabiliriz (appSettings içerisine alınacak olan key:value çiftlerinin çok kalabalık olduğu senaryolarda bu teknik oldukça işe yarayabilir) Nasıl mı? Aynen aşağıdaki ekran görüntüsünde olduğu gibi.

Burada önemli olan app.config dosyası içerisindeki appSettings elementinde kullanılan configSource niteliğidir. Bu nitelikte ilgili segmentin hangi dosyadan okunacağı belirtilir. Örnekte kullanılan ApplicationParameters.config için dikkat edilmesi gereken bir kaç nokta vardır. Bunlardan birisi sadece yerine geçecek segment içeriğini taşımasıdır. Yani sadece <appSettings> elementini bulundurmalıdır(Console.WriteLine'daki {0} yerine bir içerik geldiğini hayal edelim) Diğer yandan dosyanın mutlaka asıl konfigurasyon dosyası ile aynı yerde olması beklenir(Bu örnekte app.config ile) Dolayısıyla "Copy to Output Directory" özelliğinin "Copy if Newer" veya "Copy Always" olması gerekmektedir. Bir başka ipucunda görüşmek dileğiyle hepinize mutlu günler dilerim.

GoLang - Slice ve Map Kavramları

$
0
0

Merhaba Arkadaşlar,

Gopher olma çalışmalarım iş yoğunluğuna bağlı olarak zaman zaman hızlı ve zaman zaman da yavaş bir şekilde devam ediyor. Açıkçası Gopher karakterini ve internetteki örneklerini çok sevdim. Google oldukça sevimli bir logo yaratmış. Bu nedenle her bölüm mümkün mertebe farklı bir Gopher'ı ekranlarınıza taşımaya çalışıyorum.

Efendim gelelim sadede. Go dilinde kullanılan türlerden olan Array tipini kısaca inceledikten sonra geçtiğimiz günlerde Slice ve Map kavramlarını da incelemeye başladım. Her ikisi de sağladıkları esneklikler açısından dil içerisinde oldukça önemli bir yere sahipler. Bu makalemizde ilgili veri tiplerini basit örnekler üzerinden tanıma gayretinde olacağız.

Slice

Diziler(Arrays) pek çok programlama dilinde olduğu gibi Go için de vazgeçilmez veri türlerinden birisi. Lakin esnek olmadığı noktalar da vardır. Örneğin diziler sabit uzunluktadır ve boyutları yeni ilaveler ile dinamik olarak genişletilemez. İşin aslı dizi dediğimiz kavram sabit uzunlukta, belirli türde, indisle erişilen ve sıralanmış bir veri yapısını işaret eder. Diğer yandan slice veri türü daha esnek bir kullanım alanı sunar. Hatta dizilerin belli aralıktaki parçalarını birer slice haline getirip kullanabiliriz. Go dilinde ustalaşanlar dizi değişkenleri yerine slice türü ile çalışmayı tercih etmektedirler. Tanımlanan bir slice eleman sayısı ve kapasite ile birlikte oluşturulabilir. append fonksiyonu yardımıyla yeni elemanlar eklenebilir, copy fonksiyonu ile bir slice içeriğinin bir diğerine kopyalanması sağlanabilir. Esas itibariyle bir slice aşağıdaki basit şekil ile ifade edilebilir.

Tanımlanan bir slice değişkeni içerisinde belli türden elemanlar barındıran diziyi işaret eden bir pointer, bu dizinin uzunluğu ve kapasitesi yer alır. Yukarıdaki şekilde 5 adet integer eleman içeren maksimum kapasitesi de 10 olan numbers isimli bir slice tanımının bellekteki farazi gösterimi yer almaktadır.

Bir slice'dan vereceğimiz değer aralığına göre başka bir slice da oluşturabiliriz. Ancak burada dikkat edilmesi gereken önemli bir husus vardır. Oluşan yeni slice alt kümesi olduğu slice içerisindeki dizi elemanlarını işaret eder. Bunun anlamını biliyorsunuz. Yeni oluşan slice elemanlarında yapılacak olan değişiklikler alt kümesi olduğu slice için de geçerli olacaktır. Aşağıdaki şekilde bu durum özetlemektedir.

Şimdi basit bir kod parçası ile slice tipini nasıl kullanabileceğimize bakalım. 

package main

import (
	"fmt"
	"math"
	)
	
func main(){
	words:=[]string{"red","plane","colors","car"}
	fmt.Println(words)
	fmt.Println(words[1])
	
	var sumBytes []int // Slice tanimi
	sumBytes=make([]int,4,10) // 4 eleman icerek sekilde slice'in kurgulanmasi
	fmt.Printf("Length=%d,Capacity=%d\n",len(sumBytes),cap(sumBytes))
	fmt.Println(sumBytes)
	for i:=0;i<cap(sumBytes);i++{
		if i>=len(sumBytes){
			sumBytes=append(sumBytes,i*3)
			}else{
				sumBytes[i]=i*2
				}
	}
	fmt.Println(sumBytes)
	// slice'dan slice alinmasi halinde ortak degiskenlerin durumu
	subSumBytes:=sumBytes[3:6]
	subSumBytes[0]=-1
	subSumBytes[1]=-1
	subSumBytes[2]=-1
	fmt.Println(sumBytes)
	fmt.Println(subSumBytes)
	// diziden slice cikartmalar
	points:=[6]int{3,5,6,-4,-18,20}	 // 6 elemanli array alttaki birkac slice icin kullanilacak
	subPoints1:=points[0:3]
	fmt.Println(subPoints1)	
	subPoints2:=points[3:len(points)]
	fmt.Println(subPoints2)
	subPoints3:=points[:]
	fmt.Println(subPoints3)
	subPoints4:=points[4:]
	fmt.Println(subPoints4)
	// append
	newSlice:=make([]float32,5,10) // 5 elemanli ve max 10 kapasiteli slice tanimi
	newSlice[0]=1.32	
	newSlice=append(newSlice,math.Pi,2.277) // Slice'in sonuna eleman ekledi
	fmt.Println(newSlice)
	// Copy
	newSlice1:=[]int{3,5,1,7,8} // 5 elemanli ve kapasiteli bir slice
	newSlice2:=make([]int,3) // 3 elemanli ve kapasiteli bir slice
	copy(newSlice2,newSlice1)
	fmt.Println(newSlice1)
	fmt.Println(newSlice2)
}

Öncelikle kodun çalışma zamanı çıktısına bir bakalım ve içeriği üzerinden konuşalım.

İlk olarak en basit haliyle words isimli bir slice tanımlayarak başlıyoruz. Eleman sayısını belirtmediğimiz bu slice'ın içereceği string değerleri ilk ifadede atamaktayız. Sonrasında tüm ve sadece 1nci indisteki elamanları ekrana yazdırıyoruz. Bir başka deyişle aynı dizilerde olduğu gibi slice elemanlarına da indis operatörü ile erişebiliyoruz.

sumBytes integer değerler taşıyacak bir slice. İlk satırda eleman sayısını vermeden tanımlıyor sonraki satırda make fonksiyonunu kullanarak oluşturuyoruz. Bunu programın başında tanımlı bir slice değişkeninin ilerleyen kısımlarda oluşturabileceği manasında yorumlayabiliriz. sumBytes 4 eleman içerecek şekilde ve 10 birim kapasiteye sahip olacak şekilde tanımlanmış durumda. Slice kapasitesi ve uzunluğunu bulmak için cap ve len isimli fonksiyonlardan yararlanıyoruz. sumBytes elemanlarını ekrana yazdırdığımızda sadece 4 adet 0dan oluşan bir dizi ile karşılaşmaktayız ki bu son derece doğaldır.

Sonrasında gelen for döngüsü ise dikkate değer. Burada ilk 4 elemana indisin iki katını işaret eden sayılar ekliyoruz. Ancak i elemanı belirtilen uzunluktan fazla ise append fonksiyonuna başvuruyoruz. Neden? sumBytes için belirlediğimiz eleman uzunluğu 4. Kapasite ise 10. Buna göre örneğin 5nci elemana bir değer atamak istersek çalışma zamanında aşağıdaki ekran görüntüsünde olduğu gibi "index out of range" hatasını alırız.

Bu yüzden append fonksiyonundan yararlanmaktayız. 

subSumBytes ise sumBytes'ın bir alt kümesi gibi düşünülebilir. Bu da bir slice değişkenidir ve aslında sumBytes'ın 3ncü indisinden itibaren 4ncü ve 5nci dahil olmak üzere 6ncı indisine kadar olan elemanları işaret etmektedir. Dikkat edilmesi gereken nokta subSumBytes elemanlarında yapılan değişikliklerin doğal olarak sumBytes'ı da etkilemiş olmasıdır(-1 atamalarına dikkat edelim)

points bir array olarak tanımlanmıştır. Eleman sayısı belirli ve dizi uzunluğu sabittir. subPoints1,2,3 ve 4 bir diziden nasıl parça alınabileceğinin bir kaç örneğini barındırmaktadır. : işaretinin sol ve sağ tarafındaki girdilere göre dizinin belli bir parçasının veya tamamının([:] kullanımı) slice olarak elde edilmesi mümkündür. 

newSlice değişkeninde append fonksiyonunun kullanımı bir kez daha örneklenmiştir. 5 eleman uzunluğunda bir tanımlama yapıldığından append fonksiyonu 5nci indisteki eleman olarak dizideki yerini alacaktır. Bu nedenle [1.32 0 0 0 0 3.1415927 2.277] şeklinde bir sonuç oluşmaktadır. 

Örnek kodun son satırlarında bir slice içeriğinin(newSlice1) bir diğerine(newSlice2) kopyalanması işlemine yer verilmiştir. Burada kapasite ve eleman sayısı 5 olan bir slice'ın, 3 elemen ve kapasiteli bir slice'a kopyalanması söz konusudur. Çok doğal olarak bir trim işlemi gerçekleşir ve newSlice1'in sadece ilk 3 elemanı newSlice2'ye kopyalanır. 

Map

Gelelim bir diğer konumuz olan map veri türüne. Bu veri türünü key-value çiftleri şeklinde elemanlar barındıran bir veri yapısı olarak düşünebiliriz(Aslında Hash veri yapısının Go dilindeki built-in karşılığıdır) map veri yapısı da aynen slice ve array gibi referans türlüdür. Aynen slice kullanımında olduğu gibi make fonksiyonu ile de oluşturulabilir yada tek ifade ile elemanları atanabilir. Bir map kendisinde key-value çiftleri eklendikçe otomatik olarak büyür. map'lere eklenen şekilde bir sırlama da oluşmaz. Yani bir map içeriğini dolaştığımızda elemanlarını eklediğimiz sırada bulamayabiliriz.

key olarak tutulan veri türünün karşılaştırılabilir(comparable) olması gerekir(.net tarafında olsak IComparable gibi arayüzleri implemente eden türleri key olarak kullanabiliriz gibi bir cümleyi sarfedebilirdim lakin Go'da bu durum nasıl ele alınıyor henüz bilmiyorum. İlk okuduğum yazılara göre temel veri türlerinin key olarak kullanılabileceği yönünde) map türü Concurrent kullanımlar için güvenli(safe) değildir bu nedenle okuma ve yazma adımlarının senkronizasyonu için sync.RWMutex gibi fonksiyonlardan yararlanılır(Go dilinde Concurrent programlama konusuna ilerleyen zamanlarda bakmaya çalışacağım)Şimdi basit örnekler ile map türünün kullanımına bir bakalım.

package main

import (
	"fmt"
	)
	
func main(){
	var words map[string]string
	words=make(map[string]string) // map'i kullanabilmek icin make ile olusturmamiz gerekir.Yoksa calisma zamaninda hata aliriz
	
	words["red"]="rot"
	words["black"]="schwarz"
	words["blue"]="blau"
	words["green"]="grun"
	
	fmt.Println(words)
	fmt.Println(words["blue"])
	fmt.Println(words["empty"])
	
	// bir key degerinin map icinde olup olmadigini anlamak icin asagidaki ifadeyi kullanabiliriz
	_, isExist:=words["empty"]
	fmt.Println("words[empty] is exist = ",isExist)
	
	for key,value:=range words{
		fmt.Printf("key:%s\tvalue:%s\n",key,value)
	}
	
	// Bir map'i asagidaki gibi ayni ifadede tanimlayip elemanlarini belirleyebiliriz
	levels:=map[int]string{
		100 : "Beginners",
		200 : "Intermediate",
		300 : "Amateur",
		400 : "Pro",
		500 : "Master",
		0 : "unknown",		
	}
	WriteMapToConsole(levels)
	// map'ten eleman silmek icin delete kullanabiliriz
	fmt.Println("deleting 0")
	delete(levels,0)
	WriteMapToConsole(levels)
}

func WriteMapToConsole(m map[int]string){
	for k,v:=range m{
		fmt.Printf("key:%d -> value:%s\n",k,v)
	}
}

words isimli map değişkeni string tipinden key ve value değerleri taşıyacak şekilde tanımlanıyor. İlk satırda bir tanımlama yapıyoruz ve sonrasında make metodu ile map değişkenini oluşturuyoruz. Bu şekilde yaptığımız bir map tanımından sonra make komutunu kullanmadan eleman eklemeye kalkarsak çalışma zamanı hatası alırız(Panic :) ). words isimli map'e bir kaç key:value çifti ekliyor ve ekrana yazdırıyoruz. Olmayan bir eleman talep edildiğinde(words["empty"] gibi) geriye value'nun tipinin varsayılan değeri dönecektir. Dilersek _,isExist satırında olduğu ilgili key değerinin map içerisinde bulunup bulunmadığını kontrol ederek de ilerleyebiliriz. Bu kontrol sırasında gereksiz yere değişken ataması da yapmamış oluruz. Bir map içerisinde dönmek oldukça basittir. for döngüsünde key,value değerlerine birlikte erişme şansına sahibiz. Anahtar kelimemiz tahmin edileceği üzere range. levels isimli map değişkenini tek bir ifade içerisinde hem tanımlıyor hem de elemanlarını atıyoruz. Bir map serisinden eleman silmek için delete fonksiyonundan yararlanabiliriz.

map'ler ile ilgili enteresan konulardan birisi de value içerisinde de map'ler barındırılabileceği. Aşağıda bu durumu anlatan örnek bir kod parçasına yer veriliyor.

package main

import (
	"fmt"
	)
	
func main(){
	css:=map[string]map[string]string{
		"table" : map[string]string{
			"fontColor" : "white",
			"backgrounColod" : "blue",
		},
		"button" : map[string]string{
			"fontName" : "tahoma",
			"fonsSize" : "24",
			"fontColor" : "red",
			"backColor" : "black",
		},
	}
	
	for k,v:=range css{
		fmt.Println(k)
		for sk,sv:=range v{
			fmt.Printf("\t%s:%s\n",sk,sv)
		}
	}
}

css isimli map'in value değerleri birer map olarak tanımlanmıştır. Buna göre her bir key'e karşılık string tipinden key:value çiftleri taşıyan map'ler söz konusudur. Tüm map içeriğini içiçe for döngüsü ile dolaşmamız da mümkündür. İçteki for döngüsü dıştaki value tipini ele alacak şekilde ileri yönlü bir iterasyonu başlatabilir. Kodun çalışma zamanı çıktısı ise aşağıdaki gibi olacaktır.

Gopher olma çalışmalarımızla ilgili olarak bu yazımızda slice ve map kavramlarına değinmeye çalıştık. Bir başka makalemizde görüşünceye dek hepinize mutlu günler dilerim.

DevNot Developer Summit İstanbul

$
0
0

Merhaba Arkadaşlar,

8 Nisan'da birbirinden değerli konuşmacıların yer aldığı DevNot Developer Summit etkinliğinde kısmet olursa "Script Dillerin Önemi ve Geleceği" konulu bir sunum yapacağım. Doğruyu söylemek gerekirse benim için oldukça kazık bir konu. Script dillerdeki kısa geçmişim ve katılımcılar arasında Script diller konusunda duayen olmuş isimler olmasından dolayı biraz da çekinerek hazırlandığımı itiraf etmek isterim.

Sunumuma Script dillere hayatımın hangi noktasında ihtiyaç duyduğumu dile getirerek başlayacağım. Malum yılların .Net geliştiricisi olarak Script diller'e biraz uzağım ama o kadar da değil. Gerçek hayat örnekleri vererek konuyu izah etmeye gayret edeceğim. Sonrasında yaptığım bir takım araştırmaların sayısal sonuçlarını paylaşacağım. Ruby, Python gibi diller ile bir süredir amatör seviyede ilgilendiğimden basit bir script içeriği ile devam edeceğim. Sonrasında kendimce vardığım sonuçları değerli katılımcılarla paylaşacağım.

Etkinlik Kadir Has Üniversitesi Cibali Kampüsü D Blok'ta paralel oturumlar şeklinde iki ayrı salonda gerçekleştirilecek. Tabii öncesinde etkinliğe kayıt yaptırmanız gerekiyor. Bu adresten kayıt yaptırabilirsiniz. Ben konuşmacı olsam da yaptırdım. Onun heyecanı bir başka. Programa gelince. Program süper!

D BLOK KONFERANS SALONU

  • 09:00 Tanışma ve Etkinlik Tanıtımı
  • 09:30 Açılış Konuşması - Hakan Erdoğan
  • 10:00 Yük Altında Çalışan Web Uygulamaları Nasıl Geliştirilir? - Engin Polat
  • 10:50 Temiz Kod Tasarımı - Lemi Orhan Ergin
  • 11:40 Script Tabanlı Dillerin Önemi ve Geleceği - Burak Selim Şenyurt
  • 12:30 Ara
  • 13:00 Linux Kernel'in Yükselişi - Muhammed Cuma Tahiroğlu
  • 13:50 Kendi Kendini Ölçekleyen ve Yöneten Sistemler Geliştirmek - Gökhan Şengün
  • 14:40 Konu ve konuşmacı daha sonradan açıklanacak
  • 15:30 TypeScript ile Angular 2 Uygulamaları Geliştirmek - Bora Kaşmer

SİNEMA SALONU A

Orada görüşmek dileğiyle.

GoLang - defer, panic ve recover Kavramlarını Tanıyalım

$
0
0

Merhaba Arkadaşlar,

Gopher'ın Go diline kattığı sevimlilik ortada. Sadece maskotu değil bazı kavramları da oldukça motive edici bu dilin. Bir .Netçi olarak ortama hata fırlatmak istediğim de kullandığımız throw new Exception gibi bir terminoloji yerine panic şeklinde bir anahtar kelimenin kullanılması aslında kasttettiğim. Hatta go dilinin resmi dokümanlarında "Panic is a built-in function that stops the ordinary flow of control and begins panicking."şeklinde bir cümle ile bu terim hoş bir şekilde ifade edilmiş. En azından beni tebessüm ettirdi fotoğraftaki minion'u ise biraz ürküttü.

Geçtiğimiz günlerde panic fonksiyonunu incelerken aslında recover ve defer kavramları ile birlikte kullanımının daha anlamlı olduğunu öğrendim. Aslında amacım ortama bir istisnanın nasıl fırlatılabileceğini görmek ve hata yönetimini incelemekti. Derken kendimi defer ifadesi ile panic ve recover fonksiyonlarını araştırırken buldum. İlk etapta bu üç kavramın kod akışını kontrol etmek için kullanıldığını söyleyebiliriz. Şimdi bu kavramları örneklendirerek kısaca incelemeye çalışalım.

defer

.Net kökenli yazılımcılar için finally operasyonları amacıyla kullanılır dersek sanırım yerinde olacaktır. defer ifadesi ile işaret edilen fonksiyon, program çalışması sırasında mutlak suretle devreye girmesi istenen operasyonlarda kullanılır. Üzerinde işlem yapılmış bir dosyanın, açılan bir veritabanı bağlantısının, haberleşilen bir soket ile olan iletişimin kapatılması veya belleğe alınan ama işleri biten nesnelerin serbest bırakılması gibi genelleyebileceğimiz işlemler bu operasyonlara örnek olarak verilebilir. Tabii burada dikkat çekici nokta defer ifadesinde bildirilen fonksiyonun kodun akışında bir hata olması halinde de devreye girmesidir. Bir başka deyişle runtime panic olarak isimlendirilen çalışma zamanı hatalarının oluştuğu durumlarda defer edilen fonksiyonların çalışması söz konusudur. Kaynaklarda sıklıkla geçen dosya işlemlerinden basit bir tanesini bu bağlamda ele alalım.

package main 

import (
	"fmt" 
	"os"
)

func main() { 
	saveToFile("sometext.txt","this is gonna be the best day of my life")
}

func saveToFile(name string,content string) {
	fmt.Println("creating...")
	file,error:=os.Create(name)
	if error==nil{
		defer closeFile(file) // dogrudan defer file.Close() da denenmeli
		fmt.Fprintln(file,content)
	}else{
		return
	}
}

func closeFile(file *os.File){
	fmt.Println("closing...")
	file.Close()
	fmt.Println("closed")
}

saveToFile fonksiyonu sistem üzerinde bir dosya açıp bunun içerisinde belirtilen içeriğin yazılması ile ilgili bir işlem gerçekleştirmekte. Dosyayı oluşturmak için create operasyonundan yararlanıyoruz. Create fonksiyonundan oluşan dosya ve bir hata değişkeni dönmekte(fonksiyondan dönen değerler için çoklu atama yapıldığını fark etmişsinizdir) Eğer hata yoksa fmt paketinin FPrintln fonksiyonu ile dosyanın içerisine basit bir metin yazıyoruz. Fonksiyon ilk parametre ile dosyayı, ikinci parametre ile de içeriği alıyor.

Örnekte bakmamız gereken kısım defer ifadesinin olduğu yer aslında. closeFile isimli bir fonksiyonu işaret ediyor. Buna göre dosyanın oluşturulması ve yazılması sırasında bir hata oluşsa bile kapatma operasyonu otomatik olarak devreye girecek. Tanımlama dosya yazma işleminin öncesinde yapıldı. Bu o anda çağırılacağı anlamına gelmiyor. Aslında standart bir fonkisyon çağrısı değil burada kastedilen. Bir nevi kapatma operasyonunu garanti altına aldığımızı ifade edebiliriz. 

LIFO Durumu

defer ifadesi son giren ilk çıkar mantığına göre çalışır(Last In First Out). Buna göre bir fonksiyon içerisinde kullanılan ne kadar defer ifadesi varsa son girenden ilk eklenene göre teker teker çalıştırılır. Bu durumu anlamak için aşağıdaki kod parçasını göz önüne alalım.

package main

import(
    "fmt"
)

func main() {
    doSomething()
}

func doSomething(){
    defer subProc(100)
    defer subProc(200)
    defer subProc(300)
    
    numbers:=[]int{4,5,1,9,8}
    for _,n:=range numbers{
        fmt.Println(n)
    }
}

func subProc(i int){
    fmt.Println(i)
}

doSomething içerisinde defer ifadeleri haricinde bir slice içerisindeki elemanlarda dolaşılmaktadır. subProc içinse 3 defer ifadesi tanımlanmıştır. doSomething normal işleyişini tamamladıktan sonra içerisinde defer edilen fonksiyonlar ters sırada çalışmıştır. 

panic ve recover

Yazdığımız uygulama kodunda meydana gelebilecek bazı hatalar ortama panic olarak yansır. Bir dizinin olmayan elemanına erişilmeye çalışılması, açılmak istenen dosyanın ilgili klasörde olmaması, başaltılmadan(initialize edilmeden) bir slice içeriğinin kullanılmaya çalışılması gibi durumlar bu hatalara örnek olarak verilebilir. Geliştirici isterse çalışma zamanı için bilinçli olarak panik havası da estirebilir. Her iki durumda da paniğin oluştuğu fonksiyonun çalışması durdurulur, varsa defer edilmiş fonksiyonlar çalıştırılır ve ardından fonksiyonun sahibi olan konuma(function caller) dönülür ama devam eden kod satırları işletilmez. Oluşan panikten sakin bir şekilde çıkılması için recover fonksiyonundan yararlanılır. Ne var ki recover çağrımlarının anlamlı olması için defer iile kullanımı gerekir.

Şu ana kadar ki kodlarımızda zaman zaman da olsa panik havası esmedi değil aslında. Söz gelimi aşağıdaki kod parçası çalışma zamanında bir panik oluşmasına(panic: runtime error: index out of range) neden olur. 

package main

func main(){
	numbers:=make([]int,5)
	numbers[6]=10
}

Az önce recover ile bu tip çalışma zamanı paniklerini yatıştırabileceğimize değinmiştik. Ben tabii konuyu öğrenirken balıklama şöyle bir kod parçasını denedim.

numbers:=make([]int,5)
numbers[6]=10
err:=recover()
fmt.Println(err)

ama sonuç değişmedi. O anda recover'ın neden defer ile birlikte kullanıldığını daha iyi anlamaya başladım. Aynı kod parçasını aşağıdaki gibi düzenleyerek ilerleyelim.

package main  
import (
    "fmt" 
    )

func main() { 
    numbers:=make([]int,5)
    defer easy(numbers)
    numbers[6]=10
    fmt.Println("have fun")
}
func easy(n []int){    
    if err := recover(); err != nil {
        fmt.Printf("slice length is : %d\n",len(n))
        fmt.Printf("don't panic it's just an error\n%s",err)
	}
}

Dikkat edileceği üzere defer ifadesi ile main fonksiyonunda olası bir panik durumunda gidilebilecek bir başka fonksiyonu işaret ediyoruz. easy içerisinde recovery fonksiyonundan yararlanarak oluşan hatayı yakalayıp(eğer varsa) program akışının kontrol altına alınmasını sağlıyoruz. easy fonskiyonuna main içerisinde defer tanımını yaparken parametre geçişi de yapmaktayız(Bunu sadece parametre geçirebileceğimizi göstermek için yazdık) Bu arada defer fonksiyonunu istersek closure olarak da yazabiliriz ki yaygın kullanım şekli budur. Aynen aşağıdaki kod parçasında görüldüğü gibi.

package main  
import (
    "fmt" 
    )

func main() { 
    numbers:=make([]int,5)
    defer func(){    
        if err := recover(); err != nil {
            fmt.Printf("slice length is : %d\n",len(numbers))
            fmt.Printf("don't panic it's just an error\n%s",err)
        }
    }()
    numbers[6]=10
    fmt.Println("have fun")
}

Ancak gözden kaçmaması gereken bir nokta daha var. numbers[6]=10 ataması sonrası oluşan hata yakalanmış olsa da devam eden kod satırı işletilmedi! Yani program cidden çakıldı ve biz paniği sessiz sedasız defer ettiğimiz fonksiyon üzerinden soğukkanlı bir şekilde yatıştırdık. Olay main'de ceyeran ettiği için programdan çıkılmış olması normal. Bir .Netçi olarak bildiğimiz try...catch...finally yapısından oldukça farklı bir çalışma şekli gibi duruyor. Sanki Go dili programıcının ciddi anlamda çalışma zamanı hatası yaptıracak kod yazmasını istemiyor gibi. Aşağıdaki kod parçasını göz önüne alarak bu durumu biraz daha açalım. 

package main

import(
    "fmt"
)

func main() {
    launch()
    fmt.Println("to be continued...")
}

func launch() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("There's something wrong:", err)
        }
    }()
    fmt.Println("Start Engine ")
    startEngine()
    fmt.Println("After recovery ")
}

func startEngine() {
    panic("aaa Houston! We have a problem.")
}

main içerisinde launch isimli bir fonksiyon çağırıyoruz. Bu fonksiyon roketimizin motorlarını çalıştıran bir operasyonu kullanıyor ama başında defer ettiğimiz bir panik kontrol odası da var. startEngine içerisindeki panic fonksiyonu bilinçli olarak çalışma ortamına hata yollamak için kullanılıyor. Bu hata, defer edilen fonksiyon içerisinde yakalanıyor. İşte burası önemli. Ekrana "After recovery..." yazılmadı ama main'deki "to be continued..." basıldı. Yani hata üreten fonksiyonun çağırıcısındaki defer operasyonu devreye girdikten sonra launch işleyişinin tamamen sonlanması ve program kontrolünün main'e dönmesi söz konusu. Buna göre defer edilen fonksiyonların bir zincire eklendiğini de düşünebiliriz. 

Böylece geldik bir yazımızın daha sonuna. Bu yazımızda Go programlama dilinde program kontrol akışını değiştirmek için kullanılan defer ifadesi ile built-in gelen panic ve recover fonksiyonlarını incelemeye çalıştık. Bugüne kadar Go dile ile ilgili çalışmalardan gördüğüm kadarı ile programcının oldukça titiz kodlama yapması ve kavramlara aşina olması için oldukça fazla pratik yapması gerekiyor. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.


C# 6.0 - Collection Initializers için Add Metodunu Yönlendirmek

$
0
0

Merhaba Arkadaşlar,

Mesleki hayatımın bir döneminde yazılım eğitmeni olarak çalıştım. Ağırlıklı olarak .Net eğitimleri verdim. Tabii o zamanlar .Net nispeten daha kolaydı. Bu kadar fazla dallanan bir Framework değildi ama C# dil özellikleri de acımasızca genişlemiyordu. Aradan geçen onca yıldan sonra çalıştığım turuncu bankanın kendi akademisinden iç eğitim isteği geldi.

Konu C# programlama diliydi. İyi hazırlanmam gerektiği ortadaydı. Kurumsal projelerin standartları gereği en son teknolojiyi kullanamıyor olsak da C# 6.0 dünyasını yakalamıştık. Projelerimizi Visual Studio 2013 ile geliştirmekte ve .Net Framework 4.6.1 sürümünü kullanmaktayız(Şimdilik) Bana sanal bir makine de verdiler ve içerisine gıcır gıcır Visual Studio 2015 koydular(Kurum içinde de Visual Studio 2015 yaygınlaştırılmakta) 

Hal böyle olunca C# 6.0 ile ilgili yenilikleri gözden geçirme fırsatını da yeniden yakalamış oldum. Eğitime hazırlanırken okuduğum özelliklerden birisi de Collection Initializer olarak kullanılabilen Add metodunun istediğimiz bir başka metoda atanabilmesiydi. Bu, özellikle bir koleksiyonu sarmalladığımız IEnumerable türevli tiplerde işe yarayabilecek bir yetenek. Amacımız bir sınıf örneğini oluşturduğumuz yerde içerideki koleksiyona elemanlar atayabilmek. Bunu zaten var olan koleksiyonlar için aşağıdaki gibi yapabiliyoruz.

List<string> colors=new List<string>{
			"red",
			"blue",
			"green"
	   };

Örneğin string tipte elemanlar barındıran colors isimli List<string> nesnesini yukarıdaki kod parçasında olduğu gibi başlatabiliriz. Nesne oluşurken içerisindeki string dizisine ilk elemanlar atanıyor.

Hatta C# 6.0 ile Dictionary<T,K> tipinden koleksiyonları aşağıdaki gibi başlatabiliyoruz da(Kod Visual Studio 2015 de yazılmıştır)

var colors = new Dictionary<int, string>
{
	[100] = "Black",
	[101] = "Green",
	[102] = "Red"
};

Hedefimiz string yerine kendi tipimizi kullanıldığında benzer işlevselliği sunabilmek. Add metodunun olması bunun için yeterli ama bulunduğumuz domain gereği farklı isimli bir metoda atamak isteyebiliriz? İşte soru bu. Konuyu daha iyi anlamak için C# 6.0 öncesinden bir örnekle işe başlayalım. Elimizde aşağıdaki gibi bir sınıf olduğunu düşünelim(Bu örnek Visual Studio 2013 üzerinde geliştirildi)

public class Player
{
	public int PlayerId { get; set; }
	public string Nickname { get; set; }
	public double Level { get; set; }

	public override string ToString()
	{
		return string.Format("{0} {1} {2}", PlayerId, Nickname, Level);
	}
}

Kobay sınıflarımızdan birisi olan Player tipinden generic List<T> koleksiyonu barındıran bir diğer tipimizi de aşağıdaki gibi tasarlayalım.

public class GameSchene
	:IEnumerable<Player>
{
	private List<Player> players = new List<Player>();

	public void Assign(Player player)
	{
		players.Add(player);
	}

	public IEnumerator<Player> GetEnumerator()
	{
		return ((IEnumerable<Player>)players).GetEnumerator();
	}

	IEnumerator IEnumerable.GetEnumerator()
	{
		return ((IEnumerable<Player>)players).GetEnumerator();
	}
}

GameSchene sınıfı IEnumerable<Player> arayüzünü uygulamakta. Buna göre içerideki oyuncu listesi üzerinden for each döngüsü yardımıyla ilerleyebiliriz. Amacımız ise aşağıdaki gibi bir kod parçasını çalıştırabilmek.

class Program
{
	static void Main(string[] args)
	{
		var zone = new GameSchene
		{
			new Player{ PlayerId=102, Nickname="Maykil Cordin", Level=1000},
			new Player{ PlayerId=104, Nickname="a user", Level=800},
			new Player{ PlayerId=89, Nickname="black window", Level=750}
		};

		foreach (var p in zone)
		{
			Console.WriteLine(p.ToString());
		}
	}
}

Aslında string tipinden elemanlar barındıran generic bir kolekisyonu tek ifade içerisinde nasıl başlatabiliyorsak, benzer durumu kendi koleksiyon tipimiz için de yapmak istiyoruz. Ancak kod derlenmeyecek. Nitekim koleksiyon başlatıcısı Add metodunu bekliyor. Örnekte ise Assign metodu var. 

Pek tabii Assign metodunu Add olarak değiştirirsek kod başarılı bir şekilde derlenecek ve çalışacaktır. C# 6.0 tarafında ise Add metodunu extension olarak tanımlayıp istediğimiz başka bir metod ile ilişkilendirme şansına sahibiz. Tek yapmamız gereken aşağıdaki gibi bir genişletme sınıfı yazmak.

public static class GameScheneExtensions
{
	public static void Add(this GameSchene e, Player p) => e.Assign(p);
}

GameSchene tipi için Add isimli bir genişletme metodu söz konusudur. Expression-Bodied Functions özelliği kullanılarak => operatörü sonrası işletilmesi istenen kod tanımlanmıştır. e.Assing(p) ile Add metodunun güncel GameSchene nesne örneğindeki Assing metoduna atanması ve initialization kısmında gelen Player nesne örneğinin parametre olarak atanması sağlanmıştır. Çalışma zamanı çıktısı aşağıdaki gibidir(Örnek Visual Studio 2015 üzerinde yazılmıştır)

Basit ve ince bir özellik bilgimiz olsun mutlaka işimize yarayacaktır. Böylece geldik bir yazımızın daha sonuna. Bir başka yazımızda görüşünceye dek hepinize mutlu günler dilerim.

Tek Fotoluk İpucu 154 - C# 7.0 out İyileştirmesi

$
0
0

Merhaba Arkadaşlar,

Henüz C# 6.0'ın nimetlerini şirket projelerinde deneyimleme fırsatı bulamamışken yakın zamanda çıkan Visual Studio 2017 ile birlikte gündeme oturan C# 7.0 kabiliyetlerini yeni yeni keşfetmeye başlıyorum. C# 7.0 tarafında da epey yenilik var. Bunlardan birisi de özellikle out anahtar kelimesinin kullanımına yönelik. En yaygın senaryo string bir içeriğin sayısal tipe dönüştürülmesi sırasında TryParse fonksiyonunun kullanılması. Normal şartlarda aşağıdaki kod parçasındaki gibi gerçekleştirdiğimiz bir operasyon bu.

using System;

namespace Classics
{
    class Program
    {
        static void Main(string[] args)
        {
            string input=string.Empty;
            while((input = Console.ReadLine()).ToUpper()!="X")
            {            
                int number;
                if (Int32.TryParse(input, out number))
                {
                    Console.WriteLine("\t{0}",number);
                }
                else
                {
                    Console.WriteLine("Parse error!");
                }
            };
        }
    }
}

Örnekte Int32 tipinin TryParse metodu ile bir dönüştürme işlemi yapılmakta. TryParse bilindiği üzere string olarak gelen ilk parametreyi uygulandığı tipe dönüştürebilirse ikinci parametrede çıktı olarak veriyor. Bu teknikte out ile dışarıya verilecek değişkenin de önceden tanımlanmış olması gerekiyor. C# 7.0 da bu zorunluluk kaldırılmış durumda. Ne yazık ki şirket bilgisayarıma Visual Studio 2017'yi henüz yükletemedim. Güvenliğin önce bir bakması gerkiyormuşmuş. Ama çaresiz değiliz. Bu adresteki online derleyici pekala iş görüyor. Üzerindeki Rosyln derleyicisi sayesinde yeni dil özelliklerini deneme fırsatım oldu. Şimdi out kullanımına ilişkin bir kaç örnek yapalım.

Calculate fonksiyonu iki sayının toplamını ve farkını hesap edip out parametresi olarak geriye döndürmekte. Main metodundaki kullanım dikkatinizi çekmiştir. total ve dif isimli değişkenleri çağrım öncesi tanımlamış değiliz. Daha yaygın bir örnekle devam edelim.

TryParse metodunda out değişkenini kullanırken yine number isimli değişkeni önceden tanımlamadığımızı fark etmişsinizdir. Aslında out kullanımı sırasında değişken tipini belirtmek zorunda değiliz. Yani var tipinde bir tanımlama da mümkün. Aşağıdaki ekran görüntüsünde olduğu gibi.

Son olarak bir if bloğu içerisinde out şeklinde belirtilen değişkeni blok dışında da kullanabileceğimizi ifade edelim.

Böylece geldik bir ipucumuzun daha sonuna. Diğer C# 7.0 özelliklerini de öğrendikçe paylaşmaya çalışacağım. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

GoLang - Pointers

$
0
0

Merhaba Arkadaşlar,

1993 yılında girdiğim Matematik Mühendisliği bölümünün bilgisayar programlama ağırlıklı bir müfredatı vardı. GWBasic ile başlayan maceramızda Pascal, C, C++, Cobol gibi programlama dillerine uğradık. Sınavlarımız çoğunlukla kağıt üzerinde olurdu. Basit for döngülerini dahi kağıt üzerinde yazarak algoritma çözmeye çalışırdık. Haliyle C gibi case-sensitive kuralların katı olduğu dillerde sınavlar epey zorlu geçerdi.

Hatırladığım kadarı ile en çok zorlandığım ve hatta sonrasında kaçınmaya çalıştığım konuların başında Pointer aritmetiği geliyordu. Pointer tipini öğrenmek kolay olsa da iş aritmetiğine gelince kafam karışmıyor değildi. Zamanla yeni çıkan ve özellikle yönetimli ortamlara sahip olan dillerde bu gibi konulara pek fazla rastlayamaz olduk. Hatta C# tarafında pointer kullanımı mümkün olsa da Unsafe kod konusuna girdiği için bilinçli bir şekilde çekinerek yaklaştık. 

Ne var ki Pointer kavramı halen geçerliliğini koruyor(Google'ladığınızda bu sevimli köpek türü de karşınıza çıkıyor) Google'ın daha çok kendisi için geliştirdiği, sistem seviyesinde yüksek performanslı işler yapmak için kullandığı Go dilinde de Pointer tipi mevcut. Tek fark aritmetiğinin olmaması diyebiliriz. Gelin bu kısa yazımızda Pointer tipini tanımaya çalışalım.

Pointer bir değişkenin bellek adresini gösterir. Gösterdiği değişkenin tipinde tanımlanır. Pointer denildiğinde * ve & operatörleri ile new fonksiyonu önem kazanır. & operatörü değişken adreslerini bulmakta kullanılır. * operatörü ile Pointer'ın işaret ettiği değer alınır(Bu ise de-refere yada deference olarak anılır) new, Go ile gelen built-in fonksiyonlardan birisidir. Parametre olarak bir tip alır ve bellekte bu tipin sığacağı kadar yer ayırıp adresini geriye döndürür. Dolayısıyla içereceği değeri sonradan belli olacak bir alanın bellekte tahsis edilmesi ve adresinin yakalanması mümkündür. Elbette akıllara gelen sorulardan birisi tahsis edilen bu bellek bölgesinin işi bitince temizlenmeye ihtiyacı olup olmayacağıdır. GO çalışma zamanında bir garbage collector mekanizması söz konusudur ve bunu düşünmemize gerek yoktur.

Merhaba Pointer

İlk olarak aşağıdaki basit bir kod parçası ile işe başlayalım.

package main

import (
	"fmt"
	)
	
func main(){
	age:=22	
	pAge:=&age
	fmt.Println("Address of age",pAge)
	fmt.Println("Age(from pointer)",*pAge)
	*pAge=41
	fmt.Println("Age=",age)
	fmt.Println("Age(from pointer)",*pAge)

	point:=1000
	pPoint:=&point
	*pPoint=*pPoint*2
	fmt.Println("point=",point)
}

Olayı irdelemek için çalışma zamanı sonuçlarına bakalım.

pAge bir pointer oldu. Eşitliğin sağ tarafındaki & operatörü izleyen age değişkeninin bellek adresini yakalıyor. Bu nedenle pAge içeriğini ekrana bastığımızda bellek adresini görebiliyoruz. pAge isimli Pointer'ın işerat ettiği adres bölgesindeki değeri almak için * operatörü devreye giriyor. Sonrasında dikkat çekici bir durum söz konusu. *pAge değerinde bir değişiklik yapıp yaşa 41 sayısını atıyoruz. pAge bir pointer olduğundan üzerinden yapılan değişiklik aslında asıl işaret ettiği değişkenin değerinde gerçekleşmekte. Bu nedenle age değişkeni de artık 41 değerine sahip oluyor. point yine bir tamsayı değişkeni ve bellek adresini pPoint üzerinde taşıyoruz. * operatörü pointer'ın işaret ettiği değişken değerini verdiğinden matematik işlemlerine de dahip edebiliyoruz. *pPoint'in 2 ile çarpımında bu özellik ele alınıyor. Çok doğal olarak çarpma işlemi point değişkenini etkilemekte.

Fonksiyon Parametresi Olarak Pointer Kullanımı

İkinci örneğimizde ise pointer'ların fonksiyon parametrelerine olan etkisini inceleyeceğiz. Normalde fonksiyon parametreleri değer kopyalaması yöntemi ile kullanılırlar. Yani bir fonksiyona dışarıdan geçilen parametre değişkeni fonksiyon bloğu içerisinde kopyalanarak değerlendirirlir. Bu nedenle fonksiyon çağrımı yapılandan farklı bir değişken üzerinde işlemler yapılır. Pointer kullanıldığında ise fonksiyona geçen parametrenin adresi taşınır. Konuyu aşağıdaki örnek kod parçası ile daha iyi anlayabiliriz.

package main

import(
	"fmt"
	)
	
func main(){
	age:=41
	fmt.Println("main-age = ",age)
	fmt.Println("main-address of age",&age)
	call(age)
	fmt.Println("main-age = ",age)
	callWithPointer(&age)
	fmt.Println("main-age = ",age)
}

func call(value int){
	value+=1
	fmt.Println("call-value = ",value)
	fmt.Println("call-address of value",&value)
}

func callWithPointer(value *int){
	*value+=1
	fmt.Println("callWithPointer-value = ",*value)
	fmt.Println("callWithPointer-address of value",value)
}

Olaylar main fonksiyonunda tanımlı age isimli değişken üzerinde gelişiyor. call parametre olarak değer bazlı değişken kullanmakta. Gelen age call fonksiyonunda değiştirilse bile main'deki age değerini etkilemiyor. callWithPointer içinse durum böyle değil. callWithPointer içerisine main fonksiyonundaki age değişkeninin adresi taşınıyor. Nitekim parametre bir pointer. Bu nedenle fonksiyon içerisinde yapılan değişim main içerisindeki age değişkeninde yapılmış oluyor. Konunun iyi anlaşılması için main'deki age ve fonksiyonlardaki parametre adreslerine dikkat etmenizi öneririm. Kodun çalışma zamanı çıktısı aşağıdaki gibidir.

new ile Pointer Oluşturulması

Son olarak new fonksiyonunun kullanımına bir bakalım. 

package main

import (
	"fmt"
	)
	
func main(){
	nickP:=new(string)
	fmt.Println("Address ",nickP)
	fmt.Println("Current value",*nickP)
	*nickP="speedy gonzales"
	fmt.Printf("After assignment. '%s'\n",*nickP)
}

Örnekte new ile string tipinden nickP isimli bir Pointer tanımlanmıştır. Sadece bellekte bunun için yer ayrılmıştır. Bu nedenle atama öncesi güncel değeri yoktur. "Speedy Gonzales" atamasından sonra ise adres bölgesi veri ile doldurulmuştur. Kodun çalışma zamanı çıktısı aşağıdaki gibidir.

Immutable Struct

Immutable kavramı pek çok dilde yer alır. Bir değişkenin durumunun değiştirilemez olduğu hali temsil eder. Go dilinde örneğin string değişkenler immutable tip olarak geçerler. Yani oluşturulduktan sonra içerikleri değiştirilemez. Struct tipi de immutable özellik taşımaktadır ancak fonksiyon parametresi olarak kullanıldıklarında durum değişir. Önceden de bahsettiğimiz gibi fonksiyonlara taşınan parametreler içeride kopyalanırlar. Bu yüzden struct tipinden bir değişkeni bir Go fonksiyonuna geçtiğimizde immutable özelliğini kaybeder. Aşağıdaki kod parçasını göz önüne alalım.

package main

import (
	"fmt"
	)

func main(){
	goBook:=Book{Title:"Learning GO",Category:"Programming Languages",ListPrice:35.00}
	fmt.Printf("%s(%s),%f\n",goBook.Title,goBook.Category,newPrice(goBook))
	fmt.Printf("%f\n",goBook.ListPrice)
}

func newPrice(book Book) float32 {
	book.ListPrice+=10
	return book.ListPrice
}

type Book struct{
	Title,Category string
	ListPrice float32
}

Örnekte Book isimli bir struct kullanılıyor. Title, Category ve ListPrice isimli özellikleri var. newPrice fonksiyonu parametre olarak gelen bir kitabın liste fiyatını 10 birim arttırmakta. Dikkat edilmesi gereken nokta newPrice fonksiyonu içerisinde ListPrice değerinin değiştirilmesine rağmen myBook değişkenindeki liste fiyatının değerini korumuş olmasıdır(parametre değer türü olarak taşınmıştır)

Ancak tanımlanan struct değişkeni pointer olarak taşınırsa durum daha farklı olacaktır. * ve & operatörlerini devreye katarak kodu aşağıdaki hale getirelim.

func main(){
	goBook:=Book{Title:"Learning GO",Category:"Programming Languages",ListPrice:35.00}
	fmt.Printf("%s(%s),%f\n",goBook.Title,goBook.Category,newPrice(&goBook))
	fmt.Printf("%f\n",goBook.ListPrice)
}

func newPrice(book *Book) float32 {
	book.ListPrice+=10
	return book.ListPrice
}

ve sonuç.

Görüldüğü gibi Pointer tipinin tanımlanması ve kullanımı oldukça basittir. Böylece geldik bir Gopher olma çalışmamızın daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

DevNot - Script Tabanlı Dillerin Önemi ve Geleceği

$
0
0

Merhaba arkadaşlar,

Bir kaç ay önce yaklaşık beş yıldır çalışmakta olduğum turuncu bankanın oldukça yaşlı bir ürününde değişiklik yapmamız istendi. Söz konusu ürün bir devlet kurumunun eski nesil SNA protokol tabanlı sunucuları ile iletişim kurmaktaydı. Bankanın kritik süreçlerinde rol alan bu ürün VbScript tabanlı klasik ASP ile yazılmış bir Web uygulamasıydı. Uygulamanın neredeyse 13 yaşındaydı. Geliştiricisi olan 3ncü parti firma çok uzun zaman önce kapanmıştı.

Yenilenmesi düşünülen proje için gerekli adımlar henüz atılamadan iki önemli değişiklik talebi almıştık. Bunlardan birisi 32bitlik eski Windows Server 2003 sunucularından yeni nesil Windows Server 2012 sunucularına geçiş aşamasıydı. Dolayısıyla uygulamaların 64bitlik yeni nesil sunucularda çalışabiliyor olması gerekmekteydi. Tüm kodların gözden geçirilmesi, testlerin dev,test,pre-prod ortamları için tekrardan yapılması ve daha bir çok taşıma adımı işin içerisine giriyordu. İkincisi talep ise devlet kurumunun artık TCP/IP tabanlı yeni nesil bir iletişim protokolüne geçecek olmasıydı. Buradaki sıkıntı ise SNA tabanlı eski sistemle haberleşen ve kaynak kodu bulunmayan C tabanlı kütüphanenin 64bit işletim sisteminde çalışmıyor olması ve tabii TCP/IP tabanlı iletişimi destekleyecek bir özelliğinin bulunmamasıydı.

İlk problemde bizi zorlayacak olan kısım kod içinde kullanılan .Net 1.1 tabanlı kütüphanelerin 64bit üzerinde çalışacak şekilde ve yeni Oracle veritabanı bağlantılarına göre revize edilmesiydi. Ayrıca proje yazıldığı tarihlerde ne yazık ki veritabanı bağlantı ifadeleri kod içerisinde sabitlenmişti. Kaynak kod olmadığından var olan .net assembly'larını de-compile edip kodu çalışır hale getirip tekrar atmamız gerekiyordu. Hatta .Net 1.1 ile derleyip atmamız şarttı. Nitekim ASP tabanlı uygulamaya dokunmak o zaman dilim için çok riskliydi. TCP/IP tabanlı iletişim sorununu çözmek için biraz daha rahattık. VBScript kodlarında eski nesil iletişim protokolünü kullanan fonksiyonellikleri bulup içeride geliştirdiğimiz REST tabanlı WCF servisi çağrımı yapan kodlarla yer değiştirdik. REST servis devlet kurumu ile gerekli olan TCP/IP haberleşmesini üstleniyordu.

.Net 1.1 kütüphanesinin de-compile edilip tekrar atılmasında ise önemli bir sorunumuz vardı. Kod çok çok eski olduğundan TFS vb bir ortamda tutulmamıştı. Sadece production makinesinin benzeri olan bir pilot ortamda Notepad++ kullanarak gerekli düzenlemeleri yapıyorduk. Sunucu üzerine Visual Studio gibi bir IDE kurmamız zaten mümkün değildi. C# kodlarını düzenlemeyi başarıp komut satırından tekrar derleme işlemini gerçekleştirebilmiştik ama ASP uygulamasının kullanacağı bu bileşenin Global Assembly Cache'e atılmasında sorun yaşıyorduk. Yeni nesil windows sunucusu üzerinde varsayılan olarak yüklü gelen .Net Framework sürümleri olsun veya bizim yüklediklerimiz olsun 1.1 için gerekli GacUtil aracını içermiyordu. Peki ne yaptık?

O anda ihtiyacımız olan şey GacUtil'in yapacağı işi yaptıracak birisini bulmaktı. İmdadımıza basit bir Powershell Script'i yetişti. Bu script'ten yararlanarak derlenen bileşenimizi GAC'e atmayı başardık.

Derken akşam oldu ve evin yolunu tuttuk. El ayak çekildikten sonra ben bir süredir ilgilendiğim Ruby çalışmalarıma devam ettim. Ruby, hobi olarak ilgilendiğim ilk script dil değildi. Bankadaki Vb Script ve Powershell mecburiyetten ilgilendiğim diğer betik dillerdi. Lakin Ruby paralelinde Raspberry PI maceramız sebebiyle Python'a da bakma fırsatım olmuştu. Ufak tefek çalışmalarla bu dili de tanıyordum. Sonra işin içerisine GO girdi. Yazılımcıların yeni göz bebeklerinden olan Google destekli dili öğrenmeye çalışırken biraz zorlanıyordum. İşte hayat C#, VbScript, Powershell, Ruby, Python ve Go arasında gidip gelirken DevNot etkinliğindeki konum belirlendi.

Script dillerin önemi ve geleceği...

Her ne kadar günümüzün popüler Script dilleri arasında yer alan Javascript, PHP ve diğer çeşitli betik tabanlı Framework'ler de uzmanlığım olmasa da farklı dilleri araştırabildiğim için bir takım tahminlemelerde bulunabilirim diye düşündüm. İşte sunumum.

Sunuma hazırlanırken yorumlamalı ve derleyici tabanlı diller arasındaki farkları irdeleme fırsatı buldum. IEEE ve Stackowerflow gibi kaynakları kullanarak istatistiki bilgilere de ulaştım. "Hello World" demenin derleyici odaklı bir ortamda nasıl olduğunu ve betik dünyasında nasıl gerçekleştiğini inceledim. Tabii sadece iyi yanlarını anlatan değil, eleştirel bakış açılarına yer veren yazılara da baktım. Muhammed Cuma Tahiroğlu ve Görkem Özdoğan'ın değerli yazıları bana büyük katkı sağladı. Pek tabii en sonunda bazı sonuçlara vardım.

Sunumda çok fazla kod yazmadım aslına bakarsanız. Ruby kullanarak yorumlamalı dillerin basitliğini ifade eden aşağıdaki kod parçalarına yer verdim.

Tabii burada olaya farklı bakmak gerektiğimizi de vurgulamaya çalıştım. Ruby'de her şey bir nesne. Bu yüzden 10 yazdıktan sonra bile Fixnum sınıfının kullanılabilir metodlarına ulaşmamız mümkün. Hatta times metodundan sonra başlayan ifadede aslına parametre olarak iş yapan bir kod bloğunun gönderilmesi de söz konusu. Yani bir kod parçasını bir metoda parametre olarak geçip n defa işletilmesini sağlayabiliyor ve tüm bu kodları işletirken derleme işlemine gerek duymadan ifadeleri yorumlattırarak sonuçlar alabilyoruz. Bu beni etkilediği kadar Ruby ile ilgilenmeyen katılımcıları da oldukça etkiledi diyebilirim.

Sunumun en eğlenceli ve tek kısmı ise Powershell Script'i kullanarak Star Wars'dan Imperial March'ı çaldırdığımız kısımdı. Şöyle bir ekran görüntüsü ile paylaşayım. Evinizde gönül rahatlığı ile deneyebilirsiniz.

Umarım fikir verebildiğim ve farkındalık yaratabildiğim bir sunum olmuştur. Başka etkinliklerde görüşmek dileğiyle hepinize mutlu günler dilerim.

Tek Fotoluk İpucu 155 - C# 7.0 Tuple İyileştirmeleri

$
0
0

Merhaba Arkadaşlar,

C# 7.0 tarafında geliştiricileri mutlu eden iyileştirmelerden birisi de Tuple tipi ile ilgili. Klasik olarak bir tip tanımı yapmamıza ihtiyaç duymadan özellikle metodlardan dönüş yaptığımız noktalarda faydalanabildiğimiz generic Tuple tipinin en büyük handikapı, üye isilmendirmeleri. Aşağıdaki kod parçasında bu durumu açık bir şekilde görebiliriz.

using System;

namespace Classics
{
    class Program
    {
        static void Main(string[] args)
        {
            var result = Calculate(8.2, 4.6);
            Console.WriteLine("{0},{1},{2},{3}",
                result.Item1,
                result.Item2,
                result.Item3,
                result.Item4
                );
        }

        static Tuple<double, double, double, double> Calculate(double x, double y)
        {
            return new Tuple<double, double, double, double>(
                x+y,
                x-y,
                x*y,
                x/y
                );
        }
    }
}

Calculate isimli kobay metodumuz x ve y değişkenleri için 4 işlem yapıp sonuçları bir Tuple tipi ile geri döndürmekte. Hoşa gitmeyen nokta bu alanlara Item1,Item2,Item3 ve Item4 gibi isimlerle erişiyor olmamız. Normalde bir sınıf yazdığımızda özellik adları ile içeriğine erişmek isteriz. Üstelik bu kullanımda geliştirici dostu pek çok dildeki yazım stilinden uzak bir tanımlama şekli söz konusu(Biraz Ruby, biraz Python, az biraz Go yazınca göze hoş gelen pratik sytnax'lara alışıyor insan) Peki C# 7.0 tarafında Tuple tipi için ne gibi yenilikler söz konusu. Aşağıdaki fotoğrafta bir kısmını görebilirsiniz.

İlk göze çarpan muhtemelen Tuple diye bir anahtar kelime kullanmıyor oluşumuzdur. codes'a string türde iki değeri olan bir tuple atamaktayız. Alan adlarını belirtmediğimizden ilgili değerlere codes .Item1 ve codes.Item2 şeklinde erişebiliriz. cCodes'un tanımlanmasında ise xCode ve yCode isimli alanları içeren bir Tuple tipi söz konusudur. Dilersek ItemN adlarını eşitliğin sağ tarafında değişken değerleri verirken de belirtebiliriz. mCodes değişkeni için bu tip bir kullanım gerçekleştirilmektedir. Calculate metodu sum,dif,mul ve div isimli alan adlarını içeren bir Tuple döndürmektedir. Dikkat edilmesi gereken nokta Calculate metodunun Tuple örneğini nasıl döndürdüğüdür. result değişkenine atanan nesne örneği üzerinden sum,dif,mul ve div isimli öğelere de erişilmiştir. 

C# 7.0 da Tuple ile ismi anılan bir diğer kavram da Deconstruct metodudur. Bu yetenek sayesinde bir nesne örneğinin doğrudan bir Tuple tipine atanıp kullanılabilmesi mümkündür. Aşağıdaki fotoğrafı inceleyelim.

Product sınıfında Deconstruct isimli bir metod tanımlanmıştır. Metodda out ile belirtilen parametrelere sınıfın ProductId ve Title özelliklerinin değerleri atanmıştır. Main metodunda box isimli değişken de bu parametre yapısına uygun bir Tuple'a atanmıştır. Yani bir sınıf örneğinin Tuple türüne nasıl atanabileceğini belirtebiliriz.

Böylece geldik bir ipucumuzun daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

Ruby Kod Parçacıkları 34 - Struct ve OpenStruct

$
0
0

Merhaba Arkadaşlar,

Bir süredir şirket içinde vereceğim Ruby eğitimine hazırlanmaktayım. İşlerden çok vakit kalmasa da önceki Ruby notlarımı ve farklı kaynakları takip ederek 101 seviyesinde bir içerik oluşturmaya çalışıyorum. Gün içinde C# evde geç vakitlerde ise Ruby. Biraz yorucu olsa da oldukça keyifli aslında. Hem yeni bir şeyler öğreniyorum hem de iç eğitim gibi bir gerçek olduğundan ciddi anlamda not çıkartıyorum. Bugün konular üzerinden geçerken struct ve openstruct kavramlarını atladığımı fark ettim(Ov yooo) Tabii hemen öğrenmeye başladım. Neymiş ne için kullanılırmış biraz fikir sahibi oldum. İşte notlarım.

Struct aslında Ruby'nin built-in sınıflarından birisi. Temel olarak bir sınıf hazırlamadan nitelik ve değer barındıran tip tanımlanmasına olanak sağlıyor. Konuyu basit bir şekilde incelemeye başlamak için aşağıdaki kod parçasını göz önüne alarak ilerleyebiliriz.

player=Struct.new :firstName,:lastName,:level
dam=player.new()
dam.firstName="jan kulod van"
dam.lastName="dam"
dam.level=900
puts "#{dam.lastName}, #{dam.firstName}-[#{dam.level}]"

obiWan=player.new("kenobi","obi wan",850)
puts "#{obiWan.lastName}, #{obiWan.firstName}-[#{obiWan.level}]"

Örnek kodda player isimli bir yapı bildirimi yer alıyor. İlk tanımlama sırasında new operatörünü takiben bu veri yapısına dahil olan niteliklerin bildirimi yapılmakta. Kodumuzda aynı veri modeline sahip iki Struct değişkenine yer veriliyor. dam ve obiWan :) İlk kullanımda nitelik değerleri new operatöründen sonra atanmıştır. obiWan isimli değişken örneklenirken de ilgili nitelik değerleri new fonksiyonunda belirtilmiştir.

Yapıları tanımlarken do...end bloklarını da işin içerisinde dahil edebilir ve bu sayede çeşitli fonksiyonlar içermesini de sağlayabiliriz. Aşağıdaki kod parçasında bu durum örneklenmektedir.

book=Struct.new :title,:price,:category,:author do
  def getInfo
    "#{title} from #{author}. #{price},#{category}"
  end
end
tehlikeliOyunlar=book.new
tehlikeliOyunlar.title="Tehlikeli Oyunlar"
tehlikeliOyunlar.price=50
tehlikeliOyunlar.author="Oguz Atay"
tehlikeliOyunlar.category="Turk Edebiyati"
puts tehlikeliOyunlar.getInfo

book isimli yapı yazılırken do end blokları arasında getInfo isimli bir metod tanımına da yer verilmiştir. getInfo metodu sadece yapının niteliklerini string formunda geriye döndürmektedir. Burada sınıflardan farklı olarak niteliklere erişirken @ işaretinin kullanılmadığı gözden kaçırılmamalıdır.

Aslında bu ve bir önceki örnekleri göz önüne alırsak benzer veri yapılarını sınıf olarak tanımlamak istediğimizde aşağıdakine benzer bir yol izlememiz gerektiği ortadadır.

class Player
  attr_accessor :firstName,:lastName,:price

  def initialize(firstName,lastName,price)
    @firstName,@lastName,@price=firstName,lastName,price
  end  
  
  def getInfo
    "#{@firstName},#{@lastName},#{@price}"
  end
end

Dikkat edileceği üzere attribute tanımlamaları ve new operatörü için initialize metodunun yazımı zorunludur. Yapılar bu noktada daha pratik bir veri modeli tanımlama yolu sunmaktadır. Nitekim bir yapı initialize metodu içermemesine rağmen new operatöründe içerdiği niteliklerine değer ataması yapılabilir.

Yapılar ile ilgili dikkat çekici bir diğer nokta da OpenStruct tipinin kullanımıdır. Bu tip kullanılırken niteliklerinin baştan belirtilmesine gerek yoktur. Yani yapı istenildiği kadar nitelik barındırabilir.Nasıl mı? Aynen aşağıdaki kod parçasında görüldüğü gibi.

require "ostruct"

parameters=OpenStruct.new()
parameters.connection="provider=mysql..."
parameters.username="bsenyurt"
parameters.password="****"
parameters.timeout=6000
puts parameters.timeout
parameters.ftpAddress="ftp://localhost/images/"
puts parameters.ftpAddress

OpenStruct için ostruct bildirimi gereklidir. Sonrasında yine new operatöründen yararlanılarak bir yapı tanımlanmıştır. parameters yapısına istediğimiz kadar nitelik atayabiliriz. Örnekte programlarımızda sıklıkla başvurduğumuz ve sayısı genellikle belli olmayan parametre modeli sembolize edilmeye çalışılmıştır. Tahmin edileceği üzere OpenStruct kendi içerisinde bir hash kullanır. Bunu new ile oluşturulduğu sırada örnekler. Hash, nitelik:değer eklendikçe genişlemeye olanak sağlar.

Peki bir yapının bu örneklerde olduğu gibi n sayıda niteliğinin tamamına kolayca erişmenin bir yolu yok mudur? Tabii ki vardır. Meşhur each metodumuz ne güne duruyor. İşte örnek bir kaç kullanım.

require "ostruct"

parameters=OpenStruct.new()
parameters.connection="provider=mysql..."
parameters.username="bsenyurt"
parameters.password="****"
parameters.timeout=6000
parameters.ftpAddress="ftp://localhost/fileServer"
parameters.each_pair{|key,value| puts "#{key}->#{value}"}

player=Struct.new :firstName,:lastName,:level
obiWan=player.new("kenobi","obi wan",850)
obiWan.each{|o|puts o}
obiWan.each_pair{|key,value| puts "#{key}->#{value}"}
puts obiWan[:firstName]

Bu örnekte each, each_pair ve [] operatörü kullanımları örneklenmiştir. each ile bir yapının tüm nitelik değerlerine erişmemiz mümkündür. each_pair tahmin edileceği üzere key:value benzeri yapının nitelik adı ve değerlerine erişmekte kullanılır. İstersek bir yapının elemanlarına indeksleyici ile de ulaşabiliriz. Aslında bir yapının diziye veya hash nesnesine atanması da oldukça kolaydır. Hatta select fonksiyonundan yararlanarak bir yapının taşıdığı değerler üzerinde koşullu seçimler yapılması da sağlanabilir (Detaylar için bu ve şu adreslerdeki Ruby dokümanlarına bakmanızı öneririm)

Peki yapıların bu pratik kullanımları nedeniyle sınıflar yerine tercih edilmeleri gerekir mi? Aslında yapıların kullanım sebepleri biraz daha farklıdır. Çoğunlukla geçici bir veri yapısına(Temporary Data Structure) ihtiyaç duyduğumuzda yapılardan yararlanabiliriz. Ya da test ortamında stub nesne ihtiyacı olduğunda kullanabiliriz. Bir diğer kullanım şeklide sınıf içerisinde dahili veri modeline ihtiyaç duyduğumuz durumlardır. Aşağıdaki kod parçasında bu durum örneklenmeye çalışılmıştır.

class Employee
  attr_accessor :firstName,:lastName, :address
  Address = Struct.new(:street, :city, :country, :postal_code)

  def initialize(firstName,lastName, addressInfo)
    @firstName,@lastName=firstName,lastName
    @address = Address.new(addressInfo[:street], addressInfo[:city], addressInfo[:country], addressInfo[:postal_code])
  end

end

sitiv = Employee.new("Sitiv","Jobs", {
  street: "hevan street",
  city: "Second Parallel New York",
  country: "Beauty Country",
  postal_code: "HVN-1001"
})

puts sitiv.address.inspect

Bu örnekte Employee sınıfı içerisinde Address isimli bir yapı tanımlanmıştır. Employee sınıfına ait bir nesne örneklenirken initialize metoduna gelen son parametre bu yapıya ait bir değişken içeriğidir. Indeksleyici operatörü ile değerler alınıp Address yapısına ait değişken nitelikleri doldurulmuştur. 

Görüldüğü gibi yapılar oldukça pratik kullanıma sahip bir veri tipi olarak karşımıza çıkmaktadır. Built-In olarak sınıf kökenli olan bu tipin kullanışlı fonksiyonları bulunmaktadır. Böylece geldik bir Ruby Kod Parçacığının daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

Tek Fotoluk İpucu 156 - Sevimli Ruby Block'ları

$
0
0

Merhaba Arkadaşlar,

Farklı programlama dillerini öğrenmeye çalışırken alışkın olduğum programlama dillerindeki ortamlardan çok daha farklı kabiliyetleri görme şansı buluyorum. Bazı dillerin kabiliyetleri çok dikkat çekici oluyor. Örneğin Ruby dilindeki block kavramı. İlk tanımaya çalıştığım şu kod parçasında mevzuyu az çok anlamış olsam da asıl gücünü görmek için Ruby'nin kendi built-in yapılarındaki block kullanımlarını incelemem gerekti. Sonunda asıl faydasını anladım diyebilirim. Konuyu anlamak için dizilere sıklıkla uygulanan each, find_all, reject gibi metodları göz önüne aldım. Örneğin bir dizide belirli kurallara uyan elemanları elde etmek için kullanılan find_all metodunu kendim yazmak istesem ve bir block kullanmam gerekse bunu nasıl yapabileceğimi bulmaya çalıştım. Siz, Ruby'yi geliştiren kişi olsanız ve metodlara parametre olarak kod bloklarını geçirme yeteneğini ilave etseniz veri yapılarında bu özelliği nasıl kullandırtınız? Aşağıdaki örnek ekran çıktısında yer alan findSomething metodu gibi olabilir mi?

findSomething metodu içerisinde bir çok ders var. Örneğin bu metoda bir kod bloğu geçilip geçilmediğini anlamak için block_given? fonksiyonunu kullanıyoruz. findSomething public erişim belirleyicisine sahip. Kullanmadığımız takirde private kabul ediliyor ve çalışma zamanında NoMethodError hatası alıyoruz. Bir başka deyişle herhangibir nesneye uygulayamıyoruz. İçeride yer alan self anahtar kelimesi ile findSomething metodunun uygulandığı nesneyi yakalamaktayız. Şayet bu, dizi türevli bir nesne ise each ile elemanlarında dolaşabiliriz. yield ile tahmin edileceği üzere findSomething metoduna gelen kod bloğunu çağırıyoruz. Ayrıca yield ile findSomething metoduna gelen kod bloğuna işlenmesi için iterasyonun o anki nesnesini de(item oluyor) göndermekteyiz.

yield anahtar kelimesinin kullanıldığı yerde findSomething metodundan dışarıya çıkıp kendisine parametre olarak gelen kod bloğuna(örnekte {|n|n%3==} kısmı) geçici olarak uğradığımızı ve item değerini oraya bıraktığımızı, bloktaki iş bitince de tekrar findSomething metoduna döndüğümüzü ifade edebiliriz. Bu block'ların temel çalışma felsefesidir.

<< operatörü ile result isimli diziye eleman ataması gerçekleştirmekteyiz. Kodun findSomething operasyonunu test ettiğimiz satırında 10dan 28e kadar olan değer aralığındaki sayıların 3 ile tam bölünebilenlerini çekip r isimli bir değişkende topluyoruz. findSomething metodu, parametre olarak gelen kod bloğuna göre elde ettiği sonuçları geriye döndüren bir fonksiyon görevi üstleniyor burada. find_all metodunun tıpkısını aynısı gibi değil mi?

Aynı felsefeyi kullanarak örneğin reject isimli built-in fonksiyonu da siz yazmaya çalışabilirsiniz. Aslında Ruby ile birlikte gelen ve kod bloğu alarak çalışan fonksiyonları kendiniz yazmayı deneyerek block kavramına olan aşinalığınızı arttırabilir konuyu pek güzelce pekiştirebilirsiniz. Ruby, sevimli yetenekleri ile beni kendisine hayran bırakmaya devam ediyor. Bir başka ipucunda görüşmek dileğiyle.


DevNot Developer Summit Ankara

$
0
0

Merhaba Arkadaşlar,

8 Nisan tarihinde gerçekleştirdiğimiz DevNot Summit etkinliği arından sıradaki durağımız Ankara. Uzun zamandır ziyaret etme fırsatı bulamadığım, yedek subaylık anılarımla dolu Ankara'ya gitmek için şimdiden sabırsızlanıyorum. İstanbul etkinliğinde Script Tabanlı Dillerin Önemi ve Geleceği'nden bahsetmeye çalışmıştım. Ankara etkinliğimize de Ruby programlama dilini götürüyorum :)

Netaş, Bilketn Cyberpark, netsparker, Enqura, Qucik Execution ve Bilge Adam sponsorluğunda gerçekleştirilecek olan etkinlikte Container'lar, Angular.js, .Net Core, Typescript, Web Uygulamalarının geleceği, Linus Torvards, Web test araçları, ölçeklenebilirlik vb önemli konulara yer verilecek.

Etkinliğe şu adresten kayıt yaptırmayı unutmayınız. 

Görüşmek üzere.

JSON to BSON

$
0
0

Merhaba Arkadaşlar,

Sanıyorum her .Net programcısının takım çantasında yer alan paketlerden birisi de Newtonsoft'un JSON serileştirme kütüphanesidir. JSON(JavaScriptObjectNotation) formatı, XML(eXtensibleMarkupLanguage)şemasından sonra hafif ve az yer kaplama özellikleri nedeniyle çokça tercih edilen standartlardan birisi haline gelmiştir. Diğer yandan JSON içeriklerin Binary formatta serileştirilmiş versiyonu olarak adlandırılan BSON formatı da sıklıkla kullanılmaktadır. 

BSON, NoSQL camiasının liderlerinden MongoDB ile popülerlik kazanmış bir veri formatı. JSON içeriğinin binary formatta sunulması oldukça hızlı gerçekleşebilecek bir işlem. Bu nedenle NoSQL tabanlı sistemlerde yatay ölçeklenen veri kümeleri arasındaki iletişimde tercih edilebiliyor. Ağ üzerinde dolaşan bu küçültülmüş paketler içerisinde JSON tipinde veri bulunuyor. Zaten tasarım amaçlarından birisi de bu.

Peki .Net tarafında kullanılan nesne örneklerini BSON formatına nasıl dönüştürebiliriz? Bunun bilinen pek çok yolu var tabii ki ancak uygulamanızda Newtonsoft ve JSON içerikleri ile çalışıyorsanız işi kolaylaştıracak tiplerde bu paketle birlikte geliyor. Newtonsoft.Json paketi, bir JSON içeriğinin binary olarak yazılması ve okunması için iki temel tip sunmakta. Bu tipleri kullanarak BSON dönüşüm işlemleri kolayca gerçekleştirilebilir. Basit bir örnekle konuyu özetleyelim. 

Başlamadan önce Newtonsoft'un ilgili paketinin uygulamaya yüklenmiş olması gerektiğini hatırlatalım. Bunu NuGet Package Manager veya Console üzerinden gerçekleştirebiliriz.

Aşağıdaki sınıf diagramında ve kod parçasında görülen tipleri tasarladığımızı düşünelim.

public class Person
{
	public int PersonId { get; set; }
	public string Title { get; set; }
	public decimal Salary { get; set; }
	public Job[] Jobs { get; set; } // SOAPFormantter desteği için Array yapıldı

	public override string ToString()
	{
		return string.Format("{0}-{1}-{2}"
			, PersonId, Title, Salary.ToString("C2"));
	}
}

public class Job
{
	public int JobId { get; set; }
	public string Description { get; set; }
	public DateTime AddingTime { get; set; }
	public override string ToString()
	{
		return string.Format("\t{0}-{1}-{2}"
			, JobId, Description, AddingTime.ToShortDateString());
	}
}

Kişi bilgisini tuttuğumuz Person ve bu kişilerin önceden çalıştıkları işlerin bilgisini tutan Job isimli iki sınıfımız var. BSON dönüşümünde kullanılacak örneğin zengin olması için Person sınıfında Job tipinden  bir dizi bulunuyor. Böylece bire-çok ilişki barındıran bir nesne ağacını dönüştürme işleminde kullanabiliriz. Tabii bize test verisi ve ters serileştirme sonrası dönüşen liste içeriğini ekrana basacak fonksiyonellikler de gerekiyor. Bunları şimdilik Utility isimli bir sınıfta aşağıdaki kod parçasında görüldüğü gibi toplayabiliriz. 

public class Utility
{
	public List<Person> FillEmployees()
	{
		List<Person> employees = new List<Person>();

		employees.Add(new Person
		{
			PersonId = 1,
			Title = "Coni Minomic",
			Salary = 1900,
			Jobs = (new List<Job>
			{
				new Job{ 
					JobId=1
					, Description="Google's Master Computer Scientist"
					, AddingTime= DateTime.Now
				},
				new Job{ 
					JobId=2
					, Description="Amazon Big Cloud System Manager"
					, AddingTime= DateTime.Now
				},
			}).ToArray()
		});

		employees.Add(new Person
		{
			PersonId = 1,
			Title = "Jon Carter",
			Salary = 1780,
			Jobs = (new List<Job>
			{
				new Job{ 
					JobId=1
					, Description="Microsoft Principle Developer"
					, AddingTime= DateTime.Now
				}
			}).ToArray()
		});

		return employees;
	}

	public void WriteLine(List<Person> personOfInterest)
	{
		foreach (var person in personOfInterest)
		{
			Console.WriteLine(person.ToString());
			foreach (var job in person.Jobs)
			{
				Console.WriteLine(job.ToString());
			}
		}
	}
}

FillEmployees metodu ile test amaçlı bir çalışan listesi oluşturuyoruz. WriteLine metodu ise gelen çalışan içeriğini ekrana bastırmakla yükümlü. Amacımız, çalışan listesini BSON formatında bir dosyaya yazdırmak ve sonrasında bu içeriği ters-serileştirme ile geri okuyup ekrana yazdırmak. Bunun için gerekli örnek kod parçasını aşağıdaki gibi geliştirebiliriz.

using Newtonsoft.Json;
using Newtonsoft.Json.Bson;
using System;
using System.Collections.Generic;
using System.IO;

namespace BsonExample
{
    class Program
    {
        static void Main(string[] args)
        {
            var mario = new Utility();
            var employees = mario.FillEmployees();
            var root = Environment.CurrentDirectory;
            var file = Path.Combine(root, "employees.bson");
            JsonSerializer serializer = new JsonSerializer();

            using (MemoryStream mStream = new MemoryStream())
            {
                using (BsonWriter bsonWriter = new BsonWriter(mStream))
                {
                    serializer.Serialize(bsonWriter, employees);
                }
                File.WriteAllText(file, Convert.ToBase64String(mStream.ToArray()));
            }

            var fileContent=File.ReadAllText(file);
            var binaryContent=Convert.FromBase64String(fileContent);
            using(MemoryStream mStream=new MemoryStream(binaryContent)){
                using (BsonReader bsonReader = new BsonReader(mStream))
                {
                    bsonReader.ReadRootValueAsArray = true;
                    var result=serializer.Deserialize<List<Person>>(bsonReader);
                    mario.WriteLine(result);
                }
            }
        }
    }
	.
	.
	.

Koddaki başrol oyuncuları JsonSerializer, BsonWriter ve BsonReader sınıflarıdır. İlk using bloğunda MemoryStream ile belleğe açılan employees liste içeriğinin Base64 kodlamasından yararlanılarak dosyaya yazdırılması söz konusudur. İkinci using bloğunda ise bir kaç satır önce dosyaya yazılmış olan binary içeriğin Base64'ten byte[] haline getirilmesi ve ardından BsonReader kullanılarak generic Person listesine dönüştürülmesi işlemi yapılmaktadır. Kritik noktalardan birisi dePerson tipinden bir listenin(List<Person>) kullanılmasıdır. Eğer BsonReader nesne örneğinin ReadRootValueAsArray özelliğine true verilmezse bu listenin ters-serileştirilme aşamasında aşağıdaki ekran görüntüsünde yer alan JsonSerializationException oluşacaktır.

Kodun çalışması sırasında üretilen BSON içeriği ise aşağıdakine benzer olacaktır.

Yapılan ters serileştirme işlemi sonrası elde edilen ekran çıktısı da şöyledir. 

Görüldüğü gibi BSON içeriği başarılı bir şekilde ters-serileştirilerek nesne haline getirilebilmiştir.

Örnekteki kod parçalarını biraz daha düzenlemeye çalışmanızı önerebilirim. Nitekim BSON dönüşüm operasyonları birer genişletme metodu(extension method) haline de getirilerek kullanımları daha kolay hele getirilebilir. Ayrıca Newtonsoft'un kütüphanelerine başvurmadan da JSON içeriği oluşturabilir ve Base64 kodlamasından yararlanarak BSON içerikleri üretebilirsiniz. Sonuçta JSON standartları belli. Böylece geldik bir yazımızın daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

Tek Fotoluk İpucu 157 - GO ile Bir Queue Yazalım mı?

$
0
0

Merhaba Arkadaşlar,

Go programlama dilinde C#,Java veya benzer dillerden gelenler için söz konusu olan pek çok kolaylık bulunmayabilir. Söz gelimi Stack veya Queue gibi bir koleksiyon kullanmak istersek baştan tasarlamamız gerekebilir. Nitekim bu adresteki standart kütüphanelerde Queue ile ilgili bir şey bulamadığımı ifade edebilirim(aslında github üzerinde açık kaynak ek kütüphaneler var) Olsa da olmasa da bir queue veri yapısı tasarlayabiliriz. Hem basit ve temel bir antrenman yapmış oluruz. Aynen aşağıdaki fotoğrafta olduğu gibi(Sisteminizde benim şirket bilgisayarımda olduğu gibi GO ortamı var olmayabilir. https://play.golang.org/ adresindeki online derleyiciyi bu anlamda kullanabilirsiniz) 

Queue veri yapısı bilindiği üzere FIFO(First In First Out) ilkesine göre çalışır. Yani eklediğimiz ilk eleman ilk olarak elde edilir. Burada standartlaşmış iki fonksiyon söz konusudur. Enqueue ve Dequeue. Enqueue ile eleman eklenmesi, Dequeue ile de ilk eklenen elemanın elde edilmesi ve aynı zamanda veri yapısından çıkartılması işlemleri gerçekleştirilir.

Örnek kod parçasında işleri kolaylaştırmak adına slice tipinden yararlanılmıştır. Çünkü bu tip sahip olduğu fonksiyonellikler sayesinden otomatik olarak genişleyebilir. En azından bu kolaylığı kullanalım değil mi?

Kodda ilk olarak bir struct tanımı olduğu görülmektedir. MyQueue isimli struct içerisinde ise string tipinden bir Slice tanımlanmıştır. Tahmin edileceği üzere kuyruk string veri tipi ile çalışacak şekilde tasarlanmıştır ancak siz istediğiniz bir tipi kullanabilirsiniz. Enqueue operasyonunda yaptığımız tek şey append fonksiyonundan yararlanarak parametre olarak gelen öğeyi items listesine eklemektir. Tabii son eleman olarak eklenecektir. Enqueue, MyQueue nesnesinin adresini geriye döndürmektedir. Yani ilgili fonksiyonlar her zaman oluşturulan MyQueue üzerinde çalıştırılacaktır.

Dequeue fonksiyonunda slice içerisindeki ilk eleman(0 indisli olan) yakalanır. Sonrasında slice'ın 1nci indisinden itibaren kalan kısmı kendi üzerine tekrardan atanır. Dolayısıyla Dequeue fonksiyonu hem ilk giren elemanın döndürülmesini hem de ilk eleman sonrasında kalanların var olan slice'a atanmasını sağlar. MyQueue isimli struct içerisindeki öğelerin ekrana düzgün basılması için Println'in içinde kullandığı String fonksiyonunun yeniden yazılması yeterlidir. Bu sayede ekrana daha düzgün bir formatta içerik basılabilir.

Pek çoğunuzun MyQueue<T> gibi generic bir struct tipi olsa elimizde de kullansak dediğinizi duyar gibiyim. Bunu gerçekleştirmemiz şu anda mümkün değil gibi görünse de interface'leri kullanarak en azından tip güvenli bir veri yapısı oluşturabiliriz. Bunu denemenizi ve hatta Stack şeklinde(LIFO-Last In First Out) bir koleksiyon tipi oluşturmaya çalışmanızı önerebilirim. Kodda bazı tuzaklarda yer alıyor. Örneğin tüm elemanları çektikten sonra index out of range gibi bir hataya düşme olasılığınız yüksek.

Bunu çözmeyi deneyin.

Bir başka ipucunda görüşünceye dek hepinize mutlu günler dilerim.

Ruby Kod Parçacıkları 34 - Fiber ve Eş Zamanlı Programlama

$
0
0

Merhaba Arkadaşlar,

Eş zamanlı programlamanın(Concurrent Programming) dile veya çatıya göre farklı uygulanma şekilleri olabiliyor. Esas itibariyle genel amaç eş zamanlı olarak birden fazla işin gerçekleştirilmesini sağlayabilmek. Bu noktada en zorlayıcı noktalardan birisi işlemcinin ve işletim sisteminin bu çalışma taleplerine olan anlık tepkilerinin yönetilmesi. Neredeyse pek çok programlama ortamında Thread'ler ile karşılaşıyoruz(Bu arada yandaki fotoğrafın Ruby Fiber ile bir alakası yok. Fiber konulu imaj ararkan doğal olarak lifli yiyecekler ve sevimsiz diyet konusunu karşıma çıkmıştı)

Ruby tarafında da böyle bir yapı mevcut ama bunun dışında Fiber adı verilen farklı bir kontrol yapısı daha var. Bu nesneler daha hafif iş parçacıkları için kullanılıyor ve çoğunlukla otomatik hesaplamalar yapan yardımcı rutinler için tercih ediliyor. Tercih edilmelerindeki önemli etkenlerden birisi planlamanın geliştirici tarafından yapılması gerekliliği. Yani sanal makinenin veya önceden tanımlı bir Thread yönetim mekanizmasının kontrolünde değiller. Bu kontrol yapısı resume ve yield isimli fonksiyonları kullanıyor. resume ile bir Fiber kod bloğunun çalıştırılması sağlanıyor ve ilgili iş parçacığına abone olan diğer iş parçacığına dönebilmek için yield işlevinden yararlanılıyor. 

Çoğunlukla parçalı hesaplamaların yapıldığı, yapılan hesaplama sonuçlarının çağıran ortama geri döndürüldüğü ve tekrardan hesaplamayı yapan kod bloğuna yollanarak yeni sonuçların elde edildiği senaryolarda tercih ediliyorlar. Aslında Fiber kavramı benim yeni karşılaştığım bir konu. .Net dünyasında çoğunlukla Task Parallel Library gibi işleri oldukça kolaylaştıran yapılarla çalışıyorum. Bakalım Fiber mevzusunu kıvırabilecek miyim? Dilerseniz vakit kaybetmeden Fiber kullanımına ait basit bir kod parçası ile yola devam edelim.

Merhaba Fiber

=begin
Fiber konusuna bir bakalım
=end

myFiber=Fiber.new do
  puts "Fiber metoduna girdik"
  Fiber.yield
  puts "Fiber metodunun sonuna geldik"
end

puts "Burası çağıran kod kısmı"
myFiber.resume
puts "Tekrar çağıran koddayım"
myFiber.resume

Kodu çalıştırdığımızda aşağıdaki sonuçlar elde ederiz.

Peki ne oldu burada? Aşağıdaki grafik konuyu daha güzel özetleyebilir.

Aslında ana uygulama kodu ve Fiber nesnesi ile açılan kod bloğu arasında resume ve yield fonksiyonlarından yararlanarak geçişler yapıldığını görüyoruz. Yardımcı rutin olarak çalışmasını istediğimiz kod bloğuna geçiş yapmak veya o blokta kaldığımız yerden işlemlere devam etmek için resume, bu kod bloğunu çağıran uygulama parçacığına geri dönmek içinse yield fonksiyonundan yararlanıyoruz. Bu bir çeşit rutinler arası geçisin planlanmasıdır(Scheduling)

Çağıran ve Blok Arası Veri Alışverişi

Pek tabi Fiber blokları ve çağıran uygulama arasında veri alışverişi yapılması da gerekebilir. Yani Fiber içerisine argüman gönderilmesi ve bir takım işlemler sonucu elde edilen değerlerin döndürülmesi istenebilir. Aşağıdaki kod parçasında bu veri alışverişinin nasıl yapılabileceği basitçe örneklenmeye çalışılmıştır. 

fiberX=Fiber.new do |input|
  puts "#{input} bilgisi geldi.Şimdi bunla bir şeyler yapayım"
  Fiber.yield(rand)
  puts "{input} şeklinde yeni bir bilgi geldi. Bunu da hesaplayayım."
  Fiber.yield(rand)
  "Her şey tamamlandı"
end

puts "Çağıran kod"
output1=fiberX.resume(198)
puts "Fiber içinden #{output1} cevabı döndü"
output2=fiberX.resume(200)
puts "Fiber içinden bu kez #{output2} cevabı döndü"
puts(fiberX.resume)

Bu kez Fiber bloğuna input isimli tek bir değişken taşınıyor. Bu değişkene göre üretilen rastgele sonuçlar da çağıran tarafa iletiliyor. Kullanım oldukça basit. yield ile Fiber bloğu içinden çağıran tarafa sonuçlar dönülebiliyorken, resume ile de Fiber içerisine parametre aktarılabiliyor. Tabii planlama sırasına göre çağıran kod parçası ile Fiber kod bloğu içerisinde karşılıklı geçişler sağlanmakta. Çalışma zamanı sonuçları aşağıdaki gibidir.

Fiber Blokları Arası Veri Transferi

Şu ana kadar ki örneklerimizde Fiber ile çağıran ana uygulama arasında geçişler yaptık. Çok doğal olarak n sayıdaki Fiber bloğu arasında da veri transferi gerçekleştirmek isteyebiliriz. Nitekim bir Fiber kod bloğu tarafında yapılan hesaplamalar sırasında farklı bir Fiber kod bloğunun bu sonuçlar ile başka işlemler yapıp diğer bloğa cevap vermesi gerekebilir. Tamamen senaryoya bağlı bir durum. Biz şimdilik bu geçişlerin nasıl yapılabileceğine basit bir örnekle bakalım. İşte kod parçamız.

require 'fiber'

fiber1=fiber2=nil

fiber1=Fiber.new do |input|
  puts "Fiber 1 Başlangıç Input : #{input}"
  newInput=fiber2.transfer(input*rand)
  puts "Fiber 1e gelen yeni Input : #{newInput}"
  fiber2.transfer("işlemleri bitir")
end

fiber2=Fiber.new do |input|
  puts "Fiber 2ye gelen Input : #{input}"
  newInput=fiber1.transfer(input*rand)
  puts "Fiber1 diyor ki '#{newInput}'"
end

puts "işlemler başlıyor"
fiber1.transfer(100)
puts "işlemler bitti"

İşlemler biraz karışık gelebilir ancak ilk iki örnekteki mantığı düşünmemiz örneği anlamamız için yeterli. Fiber blokları arasındaki geçişler için yield fonksiyonu yerine fiber modülünde yer alan transfer operasyonundan yararlanıyoruz. Hatta ana kod parçasından fiber1 isimli ilk kod bloğunu başlatırken de transfer fonksiyonunu kullanmaktayız. Kodun çalışma zamanı çıktısı biraz daha iyi fikir verecektir.

Yaygın Örnek

Ruby, Fiber blokları görüldüğü üzere eş zamanlı programlamada adına basit ve hafif bir kullanım sunmakta. Eş zamanlı çalışma planlamasının programcıya bırakıldığı bu hafif yapı yardımcı rutinlerin ele alındığı senaryolarda oldukça işe yarar görünüyor. En çok verilen başlangıç örneği ise Fibonacci sayılarının hesaplanması için Fiber bloğundan yararlanılması(Hep Recursive metodları kullanırdık ama yardımcı rutin olarak bu sayıları üretecek bir iş parçacığını geliştirmeyi pek düşünmemiştik)

Fibonacci sayı dizisi 0,1 ile başlayıp her sayının kendisinden önce gelen iki sayının toplamı olarak hesaplandığı bir seridir. 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144...

Buna ait kod parçasını geliştirmeye çalışmanız, bir süre uğraşıp yapamadığınız takdirde Internetten bakmanız kişisel gelişiminiz açısından yararlı olacaktır. Aşağıdaki kod parçasına da lütfen uğraştıktan sonra bakınız.

def fibogen
  Fiber.new do
    x,y=0,1
    loop do 
      Fiber.yield(y)
      x,y=y,x+y
    end
  end
end

generator=fibogen
20.times do 
  print generator.resume,","
end

fibogen isimli fonksiyon içerisinde yeni bir Fiber bloğu oluşturulmakta. Bu blok içerisinde de sonsuz bir döngümüz var. Başlangıçta Fibonacci sayı dizisinin ilk iki değeri varsayılan olarak verilmekte(ki sayı üreticisinin bu başlangıç değerlerini parametre olarak alması daha güzel olabilir. Nitekim bu sayede istediğimiz iki sayıdan sonrasını elde etme şansını da bulabiliriz) Döngü içerisinde yield operasyonu ile çağıran koda geri dönülüyor ve o anki y değeri yollanıyor. Bu arada generator şeklinde bir değişken tanımlamassak hep 1 sayısını elde edebiliriz. Bunu bir deneyin. Ayrıca ilk 20 fibonacci sayısını bastıktan sonra örneğin ilk 5 fibonacci sayısını yakalamak istersek yeni bir fibogen değişkenine ihtiyacımız olacak(Başlangıç değerlerini parametre olarak alınnnnn) Nitekim bunu yapmassak sonraki 5 fibonacci elemanı ilk 20 elemanın arkasındakiler olarak hesaplanır.

Örneği çok daha iyi hale getirmek gerekiyor. Sınıflaştırılabilir, her yeni sayı dizisi hesaplaması için yeni bir generator üretilmemesi sağlanabilir. Bu kutsal görevleri siz değerli okurlarıma bırakıyorum. Böylece geldik bir Ruby kod parçacığımızın daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

 

GoLang - Interface Kavramı

$
0
0

Merhaba Arkadaşlar,

Geçenlerde bilgisayarımın başında oturmuş sıkılmakla meşguldüm. Her ne kadar bloğumu zinde tutmaya çalışsam da arada sırada böyle durağan dönemlere de denk geliyorum. Küçük tatiller diyelim. Derken enteresan bir Tweet yakaladım. Apollo 11'in Command ve Lunar modüllerine ait Assembler kodları github üzerinden yayına açılmış. Şu adresten bakabilirsiniz.

Yandaki fotoğrafta yer alan kadınsa Apollo 11 programının yazılım mühendisliği direktörü Margaret Hamilton. Yanında durduğu print çıktısınsa sözü geçen Assembler kodlarına ait olduğu ifade ediliyor. MIT talebeleri bu print çıktılarını üşenmeyip github'da bakılabilir hale getirmişler. Konu hakkında detaylı bir makaleye de şu adresten ulaşabilirsiniz.

Yazıyı okuduktan ve Üniversitede ders olarak gördüğüm ve orada bıraktığım Assembler kodlarına baktıktan sonra gözünü seveyim senin C#, Ruby, Go ve Python dedim. Sonrasında bu senenin planında yer alan Gopher olma çalışmalarıma devam edeyim dedim. Haydi başlayalım.

Interface metod şablonlarının koleksiyonunu tutmak için kullanılan bir veri türüdür. Burada iki önemli nokta vardır. Birincisi gövdesi olmayan metod tanımlamalarını içermesi, ikincisi ise kendisinin bir veri türü olmasıdır. Normalde .Net tarafındaki interface kavramını düşündüğümüzde nesne yönelimli programlama dillerinin temel özelliklerinden olan çok biçimlilik(Polymorphysm) ve kalıtımı(Inheritance) destekleyen bir tip olarak kullanıldığını görürüz. Bu açıdan bakıldığında bir arayüz içerisinde onu uygulayan diğer tiplerin sahip olması gereken özelliklerin ve yapması gereken aksiyonların tanımlanması söz konusudur. Ancak Go, nesne yönelimli bir dil değildir ve interface onun tip sisteminin önemli bir karakteristiğini yansıtmaktadır: Bir tipin hangi verilere sahip olması gerektiğinden ziyade hangi aksiyonları icra etmesi gerektiğinin soyutlanması. Bu yazımızda interface tipinin kullanımını basitçe ele almaya çalışacağız.

Hello World

Aşağıdaki kod parçası ile işe başlayalım. 

package main

import (
	"fmt"
	)
	
func main(){
	actors:=[]Actor{Tank{"T-80",100},Player{"Gun Ball"}}
	for _,a:=range actors{
		a.SaySomething("hello")
		a.Move("left")
	}
}

type Actor interface{
	Move(direction string)
	SaySomething(speach string)
}

type Tank struct{
	model string
	power int
}
func(t Tank) SaySomething(s string){
	fmt.Printf("'%s' says : %s\n",t.model,s)
}
func(t Tank) Move(d string){
	fmt.Printf("'%s' move to %s\n",t.model,d)
}

type Player struct{
	name string
}
func(p Player) Move(d string){
	fmt.Printf("'%s' move to %s\n",p.name,d)
}
func(p Player) SaySomething(s string){
	fmt.Printf("'%s' says %s\n",p.name,s)
}

Kodun çalışma zamanı çıktısı aşağıdaki gibidir.

Örnekte bir oyun sahnesindeki çeşitli aktörleri tanımlayan iki struct ve bir interface tipi yer almaktadır. Actor tipinde iki fonksiyon tanımına yer veriyoruz. Tank ve Player struct'larında kendilerine özgü bir kaç alan bulunuyor. Dikkat edilmesi gereken nokta bu iki struct için Actor interface'in de belirtilen metodların yazılmış olması. Sözdizimi olarak metodların ilk parametreleri uygulanacakları tipe ait. for döngüsü ile actors isimli slice elemanlarında dolaşıyor ve her biri için Move ile SaySomething metodlarını çağırıyoruz. Aslında kullanılan slice içerisindeki elemanlarda dolaşırken Go çalışma zamanı motoru interface{} değişkenine dönüştürme işlemini otomatik olarak gerçekleştirmekte. Fakat bu durum biraz sonra göreceğimiz vakka da biraz daha ilginçleşecek.

Aslında interface kullanımında Duck Typing söz konusudur. Nasıl bir şey olduğunu öğrenmek isterseniz şu yazıya göz atabilirsiniz.

interface{} Tipi

Go dilinde hiç bir fonksiyon tanımı bulundurmayan interface isimli bir tip de mevcuttur. Bu tip fonksiyon parametresi olarak kullanılabilir. Böyle bir durumda fonksiyona herhangibir tip atanabilir. Go çalışma zamanı burada bir dönüştürme işlemi gerçekleştirir. Gelen değişken interface tipine dönüştürülür. Şimdi aşağıdaki gibi bir kod yazdığımızı düşünelim.

func main(){
	actors:=[]Actor{Tank{"T-80",100},Player{"Gun Ball"}}
	DoIt(actors)
}

func DoIt(objects []interface{}){	
	for _,obj:=range objects{		
		obj.Move("Forward")
		obj.SaySomething("kuniciva")		
    }	
}

Burada actors isimli slice içeriğini DoIt fonksiyonuna interface dizisi olarak geçiyoruz. DoIt fonksiyonunda tüm nesneleri dolaşıyor ve Move ile SaySomething metodlarını sırasıyla çağırıyoruz. Bir önceki kodda yer alan for döngüsü çalıştığına göre bu fonksiyonun da çalışması gerekiyor. Oysaki Go çalışma zamanı bizi dönüştürme işleminin yapılamadığı konusunda uyaracak.

Şimdi kodun doğru halini yazalım.

func main(){
	actors:=[]Actor{Tank{"T-80",100},Player{"Gun Ball"}}
	values:=[]interface{}{actors[0],actors[1]}
	DoIt(values)
}

func DoIt(objects []interface{}){	
	for _,obj:=range objects{
		if act, ok := obj.(Actor); ok {
			act.Move("Forward")
			act.SaySomething("kuniciva")
		}
    }	
}

Öncelikle values isimli bir slice tanımı var ve interface{} tipinden oluşacağını belirtiyoruz. Elemanları ise actors'den geliyor. Bu sayede DoIt fonksiyonuna actors içeriğini interface{} tipi olarak atayabiliriz. for döngüsü içerisinde obj.(Actor) şeklinde bir çağrım var. Bu çağrım o anki interface{} tipinin bir Actor olup olmadığını kontrol etmekte. Eğer ok cevabını alırsak Move ve SaySomething metodlarını çağırabiliriz. İşte çalışma zamanı çıktısı.

Şimdi örneğimizi biraz daha geliştirelim. Actor tiplerinin yanına örneğin int tipinden bir değişken daha koyalım ve onun için geliştireceğimiz bir metodu kullanmaya çalışalım.

package main

import (
	"fmt"
	)
	
func main(){
	actors:=[]Actor{Tank{"T-80",100},Player{"Gun Ball"}}
	no:=Number(100)
	utl:=Utility(no)	
	values:=[]interface{}{actors[0],actors[1],utl}
	DoIt(values)
}

func DoIt(objects []interface{}){	
	for _,obj:=range objects{		
		switch t := obj.(type) {
			case Actor:
				t.Move("Forward")
				t.SaySomething("kuniciva")
			case Number:
				fmt.Println(t.IsEven())
		}		
    }	
}
type Utility interface{
	IsEven() bool
}
type Number int32

func(n Number) IsEven() bool{
	if int(n)%2==0{
		return true
	}else{
		return false
	}
}

IsEven metodu geriye bool değer döndüren bir metoddur ve Utility interface tipi içerisinde şablon olarak tanımlanmıştır. Bu metodu int32'den inşa edilen Number isimli yeni bir tipe uygulamaktayız. Yaptığı tek şey sayının çift olup olmadığını true veya false olarak döndürmek. DoIt fonksiyonunun kullanımı sırasında utl isimli bir değişkenin eklendiği gözden kaçmamalıdır. Aslında burada Number tipinin Utility interface{} tipine dönüştürülmesi söz konusudur. Sonrasında DoIt fonksiyonuna parametre olarak geçilir. DoIt fonksiyonu içerisinde bu kez bir switch bloğuna yer verilmiştir. switch bloğunda yaptığımız şey tipe bakıp akışı yönlendirmekten ibaret. Gelen tip bir Actor ise Move ve SaySomething metodları çağırılır. Gelen tip Number ise de IsEven. Kodun çalışma zamanı çıktısı aşağıdaki gibi olacaktır. 

interface veri tipi esasında iki parçadan oluşur. Parçalardan biri veriyi tutan değişkeni işaret eden bir pointer barındırır. Diğer parçaysa interface'in bu ilişkili tip üzerinden çağırabileceği fonksiyon bilgisini barındıran veri tablosunu işaret eder.

Pointer Kullanımı

interface içinde tanımlı metodları ilgili tipler için yazarken parametrelerinde Pointer da kullanabiliriz. İlk yazdığımız örneği göz önüne alırsak metod parametrelerinde Tank ve Player tipleri için * operatörü ile bu struct'lara ait nesne örneklerini interface tipine dönüştürdüğümüz yerde & operatörünü kullanmamız yeterlidir(*'ın & kullanımı halinde zorunlu olmadığını da biraz sonra göreceğiz) İlk örnek kodumuzu aşağıdaki hale getirerek ilerleyelim.

package main

import (
	"fmt"
	)
	
func main(){
	actors:=[]Actor{&Tank{"T-80",100},&Player{"Gun Ball"}}
	for _,a:=range actors{
		a.SaySomething("hello")
		a.Move("left")
	}
}

type Actor interface{
	Move(direction string)
	SaySomething(speach string)
}

type Tank struct{
	model string
	power int
}
func(t *Tank) SaySomething(s string){
	fmt.Printf("'%s' says : %s\n",t.model,s)
}
func(t *Tank) Move(d string){
	fmt.Printf("'%s' move to %s\n",t.model,d)
}

type Player struct{
	name string
}
func(p *Player) Move(d string){
	fmt.Printf("'%s' move to %s\n",p.name,d)
}
func(p *Player) SaySomething(s string){
	fmt.Printf("'%s' says %s\n",p.name,s)
}

Move ve SaySomething metodlarında *Tank ve * Player şeklinde Pointer kabul eden parametreler kullanıyoruz. Ayrıca actors isimli slice içerisindeki atamalarda & operatöründe yararlandık. Bu sayede adres bilgisini metodlara göndermiş oluyoruz. Çalışma zamanı çıktısı aşağıdaki gibi olacaktır.

Eğer & operatörünü kullanmazsak çalışma zamanında hata mesajı alırız. 

Bu son derece doğaldır nitekim Move ve SaySomething metodları birer Pointer beklemektedir. Ancak biz değer göndermeye çalışıyoruz. Şimdi de tam tersi durumu ele alalım. Yani metod şablonlarında Pointer kullanımından vazgeçip sadece slice içerisinde & ile adres ataması gerçekleştirelim. Çalışma zamanında hata oluşmayacak ve kod başarılı bir şekilde çalışacaktır. Bunun sebebi Pointer tipinin ilişkilendirildiği tipin üyelerine(bu örnekte Move ve SaySomething metodlar) erişebilmesidir.

Bu son iki örnekteki farkları anlamak önemlidir. Go dilinde varsayılan olarak fonksiyon parametreleri veri kopyalama yöntemi ile kullanılırlar. Yani çağrım yapılan yerden gönderilen parametre verisi, fonksiyon içinde kullanılmak için kopyalanır. Bu nedenle Pointer parametre alan fonksiyona değer türü şeklinde atama yaptığımızda hata alırız. Çünkü beklenen Tank veya Player tipinden bir değişken adresidir. Diğer yandan Pointer tipinden parametre almayan fonksiyona & operatörü ile veri gönderdiğimizde adres kopyalaması söz konusu olacağından, interface tipinin tanımlı üyelerine(Move ve SaySomething) erişebiliriz.

Görüldüğü gibi interface kavramı basit görünen ama detayına inildikçe dikkat edilmesi gereken özellikler taşıyan bir kavramdır. Go dili ile ilgili kavramları çalıştıktça paylaşmaya devam edeceğim. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

Viewing all 351 articles
Browse latest View live