Arayüz
Go dilinde, arayüz bir yöntem imzaları kümesini tanımlayan ancak yöntemlerin uygulamasını sağlamayan soyut bir türdür. Arayüzün temel felsefesi davranışı tanımlamaktır. Somut davranış uygulaması arayüzü uygulayan tür tarafından sağlanır. Arayüzler Go dilinde çok biçimlilik, gevşek bağlantı ve kod yeniden kullanımı için yaygın olarak kullanılır.
Kavram
Go'nun arayüz gelişim tarihinde bir dönüm noktası vardır. Go1.17 ve öncesinde, resmi referans kılavuzunda arayüz tanımı: bir yöntemler kümesi olarak tanımlanmıştır.
An interface type specifies a method set called its interface.
Arayüz uygulamasının tanımı
A variable of interface type can store a value of any type with a method set that is any superset of the interface. Such a type is said to implement the interface
Türkçeye çevirirsek, bir türün yöntemler kümesi bir arayüzün yöntemler kümesinin üst kümesi olduğunda ve bu türün değeri bu arayüz türü değişkeni tarafından saklanabildiğinde, bu türün bu arayüzü uyguladığı söylenir.
Ancak Go1.18'de, arayüz tanımı değişti. Arayüz bir türler kümesi olarak tanımlandı.
An interface type defines a type set.
Arayüz uygulamasının tanımı
A variable of interface type can store a value of any type that is in the type set of the interface. Such a type is said to implement the interface
Türkçeye çevirirsek, bir tür bir arayüzün tür kümesinde bulunduğunda ve bu türün değeri bu arayüz türü değişkeni tarafından saklanabildiğinde, bu türün bu arayüzü uyguladığı söylenir. Ayrıca aşağıdaki ek tanımlar verildi.
Aşağıdaki durumlarda, T türünün I arayüzünü uyguladığı söylenebilir
- T bir arayüz değildir ve I arayüzü tür kümesinin bir elemanıdır
- T bir arayüzdür ve T'nin tür kümesi I arayüzü tür kümesinin bir alt kümesidir
Eğer T bir arayüzü uyguluyorsa, T'nin değeri de bu arayüzü uygular.
Go'nun 1.18'deki en büyük değişikliği jenerik eklemesiydi. Yeni arayüz tanımı jenerik için hizmet eder. Ancak önceki arayüz kullanımını etkilemez. Aynı zamanda arayüzler iki kategoriye ayrılır:
- Temel Arayüz (
Basic Interface): Sadece yöntemler kümesi içeren arayüz temel arayüzdür - Genel Arayüz (
General Interface): Tür kümesi içeren herhangi bir arayüz genel arayüzdür
Yöntemler kümesi nedir? Yöntemler kümesi bir yöntemler koleksiyonudur. Aynı şekilde, tür kümesi bir türler koleksiyonudur.
::: ipucu
Bu kavramın karmaşık ve anlaşılması zor olduğunu düşünebilirsiniz. Ancak aslında yukarıdaki bu uzun paragrafı tamamen anlamanıza gerek yoktur.
:::
Temel Arayüz
Önce temel arayüzün yöntemler kümesi olduğunu öğrendik, yani bir yöntemler koleksiyonudur.
Bildirim
Önce arayüzün nasıl göründüğüne bakalım.
type Person interface {
Say(string) string
Walk(int)
}Bu bir Person arayüzüdür. İki dışa açık yöntemi vardır: Walk ve Say. Arayüzde, fonksiyon parametre adları artık önemli değildir. Elbette parametre adları ve dönüş değeri adları eklemek de izin verilir.
Başlatma
Sadece arayüz başlatılamaz. Çünkü sadece bir spesifikasyondur, somut uygulama yoktur. Ancak bildirilebilir.
func main() {
var person Person
fmt.Println(person)
}Çıktı
<nil>Uygulama
Önce bir örneğe bakalım. Bir inşaat şirketi özel bir vinç spesifikasyonu istiyor. Bu nedenle vincin özel spesifikasyonlarını ve çizimlerini veriyor ve vincin kaldırma ve yükleme işlevlerine sahip olması gerektiğini belirtiyor. İnşaat şirketi vinç yapmaktan sorumlu değil, sadece bir spesifikasyon veriyor, buna arayüz denir. Şirket A siparişi kabul etti, kendi şirketinin benzersiz teknolojisine göre eşsiz bir vinç üretti ve inşaat şirketine teslim etti. İnşaat şirketi hangi teknoloji ile uygulandığını umursamıyor, eşsiz vinç umurlarında değil. Sadece kaldırma ve yükleme işlevlerine sahip olması yeterli. Sadece sıradan bir vinç olarak kullanıyor. Spesifikasyona göre somut işlevler sağlamak, buna uygulama denir. Arayüzün spesifikasyonuna göre işlevleri kullanmak, iç uygulamayı gizlemek, buna arayüz odaklı programlama denir. Bir süre sonra, eşsiz vinç arızalandı, şirket A da kaçtı. Şirket B spesifikasyona göre daha güçlü bir dev vinç üretti. Aynı kaldırma ve yükleme işlevlerine sahip olduğundan, eşsiz vinç ile sorunsuz bir şekilde bağlanabilir. Önceki kullanımı etkilemez, inşaat ilerlemesini etkilemez. İnşaat başarıyla tamamlandı. İç uygulama değişir ancak işlev değişmez, önceki kullanımı etkilemez, isteğe bağlı olarak değiştirilebilir. İşte arayüz odaklı programlamanın avantajı.
Sonraki durumda Go ile yukarıdaki senaryoyu açıklayacağız
// Vinç arayüzü
type Crane interface {
JackUp() string
Hoist() string
}
// Vinç A
type CraneA struct {
work int //İç alanlar farklıdır, iç detayların farklı olduğunu temsil eder
}
func (c CraneA) Work() {
fmt.Println("Teknoloji A kullan")
}
func (c CraneA) JackUp() string {
c.Work()
return "jackup"
}
func (c CraneA) Hoist() string {
c.Work()
return "hoist"
}
// Vinç B
type CraneB struct {
boot string
}
func (c CraneB) Boot() {
fmt.Println("Teknoloji B kullan")
}
func (c CraneB) JackUp() string {
c.Boot()
return "jackup"
}
func (c CraneB) Hoist() string {
c.Boot()
return "hoist"
}
type ConstructionCompany struct {
Crane Crane // Sadece Crane türüne göre vinç depolar
}
func (c *ConstructionCompany) Build() {
fmt.Println(c.Crane.JackUp())
fmt.Println(c.Crane.Hoist())
fmt.Println("İnşaat tamamlandı")
}
func main() {
// Vinç A kullan
company := ConstructionCompany{CraneA{}}
company.Build()
fmt.Println()
// Vinç B'yi değiştir
company.Crane = CraneB{}
company.Build()
}Çıktı
Teknoloji A kullan
jackup
Teknoloji A kullan
hoist
İnşaat tamamlandı
Teknoloji B kullan
jackup
Teknoloji B kullan
hoist
İnşaat tamamlandıYukarıdaki örnekte, arayüz uygulamasının örtük olduğunu gözlemleyebilirsiniz. Resmi temel arayüz uygulamasının tanımına da karşılık gelir: Yöntemler kümesi arayüzün yöntemler kümesinin üst kümesidir. Bu nedenle Go'da bir arayüzü uygulamak için implements anahtar kelimesini açıkça belirtmeye gerek yoktur. Bir arayüzün tüm yöntemlerini uyguladığınız sürece, bu arayüzü uygulamış olursunuz. Uygulamadan sonra, arayüz başlatılabilir. İnşaat şirketi yapısı içinde bir Crane türü üye değişkeni bildirilir. Crane arayüzünü uygulayan tüm değerleri saklayabilir. Crane türü değişkeni olduğundan, erişilebilen yöntemler sadece JackUp ve Hoist'tir. Diğer yöntemler örneğin Work ve Boot erişilemez.
Önceki bölümde herhangi bir özel türün yönteme sahip olabileceğinden bahsetmiştik. Uygulama tanımına göre, herhangi bir özel tür arayüzü uygulayabilir. Aşağıda birkaç özel örneğe bakalım.
type Person interface {
Say(string) string
Walk(int)
}
type Man interface {
Exercise()
Person
}Man arayüzünün yöntemler kümesi Person'ın üst kümesidir. Bu nedenle Man de Person arayüzünü uygular. Ancak bu daha çok bir "kalıtım" gibidir.
type Number int
func (n Number) Say(s string) string {
return "bibibibibi"
}
func (n Number) Walk(i int) {
fmt.Println("yürüyemez")
}Number türünün alt türü int'tir. Bu diğer dillerde çılgınca görünse de, Number'ın yöntemler kümesi gerçekten de Person'ın üst kümesidir. Bu nedenle uygulama sayılır.
type Func func()
func (f Func) Say(s string) string {
f()
return "bibibibibi"
}
func (f Func) Walk(i int) {
f()
fmt.Println("yürüyemez")
}
func main() {
var function Func
function = func() {
fmt.Println("bir şey yap")
}
function()
}Aynı şekilde, fonksiyon türü de arayüzü uygulayabilir.
Boş Arayüz
type Any interface{
}Any arayüzü içinde yöntemler kümesi yoktur. Uygulama tanımına göre, tüm türler Any arayüzünün uygulamasıdır. Çünkü tüm türlerin yöntemler kümesi boş kümenin üst kümesidir. Bu nedenle Any arayüzü herhangi bir türün değerini saklayabilir.
func main() {
var anything Any
anything = 1
println(anything)
fmt.Println(anything)
anything = "something"
println(anything)
fmt.Println(anything)
anything = complex(1, 2)
println(anything)
fmt.Println(anything)
anything = 1.2
println(anything)
fmt.Println(anything)
anything = []int{}
println(anything)
fmt.Println(anything)
anything = map[string]int{}
println(anything)
fmt.Println(anything)
}Çıktı
(0xe63580,0xeb8b08)
1
(0xe63d80,0xeb8c48)
something
(0xe62ac0,0xeb8c58)
(1+2i)
(0xe62e00,0xeb8b00)
1.2
(0xe61a00,0xc0000080d8)
[]
(0xe69720,0xc00007a7b0)
map[]Çıktıdan, iki tür çıktının tutarsız olduğunu görebilirsiniz. Aslında arayüz (val,type) oluşan bir demet olarak düşünülebilir. type somut türdür. Yöntem çağrılırken somut türün somut değeri çağrılır.
interface{}Bu da bir boş arayüzdür. Ancak anonim bir boş arayüzdür. Geliştirme sırasında genellikle anonim boş arayüz kullanılarak herhangi bir türün değerini almak için kullanılır. Örnek aşağıdaki gibidir
func main() {
DoSomething(map[int]string{})
}
func DoSomething(anything interface{}) interface{} {
return anything
}Sonraki güncellemelerde, resmi başka bir çözüm önerdi. Kolaylık sağlamak için, any kullanılarak interface{} yerine kullanılabilir. İkisi tamamen eşdeğerdir. Çünkü ilki sadece bir tür takma adıdır. Aşağıdaki gibi
type any = interface{}Boş arayüz karşılaştırılırken, alt türü karşılaştırılır. Tür eşleşmezse false olur. İkincisi değer karşılaştırmasıdır. Örneğin
func main() {
var a interface{}
var b interface{}
a = 1
b = "1"
fmt.Println(a == b)
a = 1
b = 1
fmt.Println(a == b)
}Çıktı
false
trueEğer alt tür karşılaştırılamazsa, panic oluşur. Go için, yerleşik veri türlerinin karşılaştırılabilir olup olmadığı aşağıdaki gibidir
| Tür | Karşılaştırılabilir | Temel |
|---|---|---|
| Sayı türleri | Evet | Değer eşit mi |
| String türleri | Evet | Değer eşit mi |
| Dizi türleri | Evet | Dizinin tüm elemanları eşit mi |
| Slice türleri | Hayır | Karşılaştırılamaz |
| Yapı | Evet | Alan değerleri tamamen eşit mi |
| map türleri | Hayır | Karşılaştırılamaz |
| Kanal | Evet | Adres eşit mi |
| İşaretçi | Evet | İşaretçinin sakladığı adres eşit mi |
| Arayüz | Evet | Altta saklanan veri eşit mi |
Go'da tüm karşılaştırılabilir türleri temsil eden özel bir arayüz türü vardır: comparable
type comparable interface{ comparable }::: ipucu
Karşılaştırılamayan türleri karşılaştırmaya çalışırsanız, panic oluşur.
:::
Genel Arayüz
Genel arayüz jenerik için hizmet eder. Jeneriği öğrendiğiniz sürece, genel arayüzü de öğrenmiş olursunuz. Lütfen Jenerik bölümüne gidin.
