Verse’te sınıf, benzer davranış ve özelliklere (alan ve yöntemler) sahip objeler oluşturmak için kullanılan bir şablondur. Bileşik türdedir, yani diğer türlerde paketlenmiş veriler ve bu veriler üzerinde çalışabilecek fonksiyonlardan oluşur.
Sınıflar hiyerarşiktir, yani bir sınıf, üstünden (üst sınıfından) devralabilir ve bilgilerini altları (alt sınıfları) ile paylaşabilir. Sınıflar, kullanıcı tarafından tanımlanan özel bir tür olabilir. Örnekle karşılaştır.
Örneğin, oyununda birden fazla kedi olmasını istediğini varsayalım. Bir kedinin bir adı ve yaşı vardır ve kedi miyavlayabilir. Kedi sınıfın şöyle görünebilir:
cat := class:
Name : string
var Age : int = 0
Sound : string
Meow() : void = DisplayMessage(Sound)Sınıf içinde iç içe yerleştirilmiş olan değişkenlerin tanımları, sınıfın alanlarını tanımlar. Bir sınıf içinde tanımlanan fonksiyonlar, metotlar olarak da adlandırılabilir. Alanlar ve metotlar sınıf üyeleri olarak adlandırılır. Yukarıdaki örnekte, Sound (ses) bir alandır, Meow ise bir cat (kedi) metodudur.
Bir sınıfın alanları varsayılan bir değere sahip olabilir veya olmayabilir veya yalnızca alanın sahip olabileceği değerleri sınırlayan bir tür tanımlayabilir. Yukarıdaki örnekte, Ad varsayılan bir değere sahip değilken Yaş varsayılan bir değere sahiptir. Değer, <converges> efektlere sahip olan bir ifade kullanılarak belirtilebilir. Varsayılan değer ifadesi, Self tanımlayıcısını kullanmayabilir. Aşağıda bu tanımlayıcı hakkında bilgi alabilirsin.
Örneğin, kedilerinin başlarını eğebilmesini istediğini varsayalım. Aşağıdaki kodda, IdentityRotation() metodunu kullanarak bir ilk dönüş HeadTilt’i başlatabilirsin çünkü metot, <converges> belirticisine sahip olup hiçbir yan etki olmadan tamamlanması garanti edilir.
cat := class:
...
# A valid expression
HeadTilt:rotation = IdentityRotation()Bir Sınıf Oluşturma
Bir kedinin ne olduğunu ve neler yapabileceğini tanımlayan bir sınıfla, bir arketipten sınıfın bir örneğini oluşturabilirsin. Bir arketip, sınıf alanlarının değerlerini tanımlar. Örnek olarak, cat sınıfından Percy adında yaşlı bir kedi yapalım:
OldCat := cat{Name := ”Percy”, Age := 20, Sound:= ”Rrrr”}Bu örnekte arketip, { and } arasındaki kısımdır. Sınıfın tüm alanları için değer tanımlaması gerekmez, ancak en azından varsayılan değeri olmayan tüm alanlar için değer tanımlaması gerekir. Alanlardan herhangi bir atlanırsa oluşturulan örnek o alan için varsayılan değere sahip olacaktır.
Bu durumda, cat sınıfı Age (yaş) alanı, kendisine atanmış olan (0) varsayılan değerine sahiptir. Alanın varsayılan bir değeri olduğundan bir sınıf örneği oluştururken bu alan için bir değer belirlemen gerekmez. Alan bir değişkendir, yani oluşturma sırasında bir değer belirlesen de söz konusu değişkenin değeri oluşturma sonrasında değiştirilebilir.
Buna karşılık, cat’in Name (ad) alanı değiştirilebilir bir değişken olmadığından varsayılan olarak sabittir. Buna göre, bu alan için oluşturma sırasında varsayılan bir değer belirleyebilir, ancak oluşturma sonrasında söz konusu değeri değiştiremezsin, dolayısıyla bu alan sabittir.
Verse’te bir sınıf bir şablon olduğundan cat sınıfından istediğin kadar örnek oluşturabilirsin. Şimdi Flash adında bir yavru kedi yapalım:
Kitten := cat{Name := ”Flash”, Age := 1, Sound := ”Mew”}Alanlara Erişmek
Artık bazı cat örneklerine sahip olduğuna göre her kedinin Name alanına OldCat.Name veya Kitten.Name ile erişebilir ve her kedinin Meow metodunu OldCat.Meow() veya Kitten.Meow() ile çağırabilirsin.
Her iki kedi de aynı adlandırılmış alanlara sahiptir, ancak bu alanların farklı değerleri vardır. Örneğin, OldCat.Meow() ile Kitten.Meow() farklı davranırlar, çünkü Sound alanları farklı değerlere sahiptir.
Self
Verse’te Self, bir sınıf metodunda metodun çağrıldığı sınıfın örneğine başvurmak için kullanılabilen özel bir tanımlayıcıdır. Metodun çağrıldığı örneğin diğer alanlarına Self kullanmadan başvurabilirsin ancak örneğe bir bütün olarak başvurmak istersen Self kullanman gerekir.
Örneğin, DisplayMessage, bir mesajın hangi evcil hayvanla ilişkilendirileceğine yönelik bir bağımsız değişken gerektiriyorsa:
DisplayMessage(Pet:pet, Message:string) : void = …
cat := class:
…
Meow() : void = DisplayMessage(Self, Sound)Kedinin miyavlamasının daha yüksek sesli bir versiyonunu başlatmak istersen bunun için zaten ayarlamış olduğun Sound değişkenini kullanabileceğini düşünebilirsin. Ancak bu, aşağıdaki kodda işe yaramayacaktır, çünkü LoudSound, varsayılan değer ifadeleri, Self tanımlayıcısını kullanamayacağından Sound örnek üyesine referans veremez.
cat := class:
...
Sound : string
Meow() : void = DisplayMessage(Self, Sound)
# The following will fail since default class values
# can't reference Self
LoudSound : string = "Loud " + Self.Sound
LoudMeow() : void = DisplayMessage(Self, LoudSound)Alt Sınıflar ve Devralma
Sınıflar, devralan sınıftaki üst sınıfın tüm alanlarını içeren bir üst sınıftan devralabilir. Bu tip sınıfların üst sınıfın bir alt sınıfı olduğu belirtilir. Örneğin:
pet := class:
Name : string
var Age : int = 0
cat := class(pet):
Sound : string
Meow() : void = DisplayMessage(Self, Sound)
dog := class(pet):
Trick : string
Burada, cat ve dog (köpek) tanımlanırken class(pet)’in kullanımı, bunların pet (evcil hayvan) sınıfından devraldıklarını bildirir. Bunlar diğer bir deyişle, pet’in alt sınıflarıdır.
Bunun birkaç avantajı vardır:
Hem kedilerin hem de köpeklerin adları ve yaşları olduğundan bu alanların
petsınıfında yalnızca bir kez tanımlanması gerekir. Hemcathem dedogalanı bu alanları devralacaktır.petsınıfı,pet’in herhangi bir alt sınıfının örneğine başvurmak için bir tür olarak kullanılabilir. Örneğin, yalnızca bir evcil hayvanın adını gerektiren bir fonksiyon yazmak istiyorsan fonksiyonu hem kediler hem de köpekler için ve gelecekte ekleyebileceğin diğerpetalt sınıfları için bir kez yazabilirsin:VerseIncreaseAge(Pet : pet) : void= set Pet.Age += 1
Daha fazla bilgi için alt sınıflar sayfasını inceleyebilirsin.
Geçersiz Kılmalar
Bir alt sınıf tanımladığında, üst sınıfta tanımlanmış olan alanları, bunların türlerini daha belirgin hale getirmek veya varsayılan değerlerini değiştirmek için geçersiz kılabilirsin. Bu işlemi yapmak için, alt sınıfındaki alanın tanımını, adında <override> belirleyicisi olacak şekilde yeniden yazman gerekir. Örneğin, pet’e varsayılan değeri 1 olan bir Lives (can) alanı ekleyebilir ve kediler için varsayılan değeri geçersiz kılıp 9 yapabilirsin:
pet := class:
…
Lives : int = 1
cat := class(pet):
…
Lives<override> : int = 9Metot Çağrıları
Bir sınıf örneğinin bir alanına eriştiğinde, o örneğin alan için değerine erişmiş olursun. Yöntemler için alan bir işlevdir ve geçersiz kılındığında alanın değeri yeni bir işlevle değiştirilir. Bir yöntemin çağrılması, alanın değerini çağırır. Bunun anlamı, çağrılan yöntemin örnek tarafından belirlenmesidir. Aşağıdaki örneği değerlendir:
pet := class:
…
OnHearName() : void = {}
cat := class(pet):
…
OnHearName<override>() : void = Meow()
dog := class(pet):
…
CallFor(Percy) yazarsan cat tarafından tanımlanan OnHearName metodunu çağıracaktır. Fido’nun dog sınıfının bir örneği olduğu durumda CallFor(Fido) yazarsan dog tarafından tanımlanan OnHearName metodunu çağıracaktır.
Görünürlük Belirleyicileri
Sınıf alanları ile metotlara, bunlara kimin erişimi olacağını kontrol etmek için görünürlük belirleyicileri ekleyebilirsin. Örneğin, private belirleyicisini Sound alanına ekleyebilirsin, böylece bu özel alana yalnızca sahip olan sınıf erişebilir.
cat := class:
…
Sound<private> : string
MrSnuffles := cat{Sound := "Purr"}
MrSnuffles.Sound # Error: cannot access a private fieldSınıflarla kullanabileceğin tüm görünürlük belirleyicileri aşağıdadır:
Public: Sınırsız erişim.
Internal: Erişim geçerli modülle sınırlıdır. Bu varsayılan görünürlüktür.
Protected: Erişim geçerli sınıf ve varsa alt sınıflar ile sınırlıdır.
Private: Erişim geçerli sınıfla sınırlıdır.
Erişim Belirleyicileri
Erişim belirleyicilerini kimlerin oluşturabileceğini kontrol etmek için bu belirleyicileri bir sınıfa ekleyebilirsin. Bunu yapmak, mesela bir sınıf örneğinin yalnızca belirli bir bağlamda oluşturulabildiğinden emin olmak istiyorsan faydalıdır.
pets := module:
cat<public> := class<internal>:
Sound<public> : string = "Meow"
GetCatSound(InCat:pets.cat):string =
return InCat.Sound # Valid: References the cat class but does not call its constructor
MakeCat():void =
MyNewCat := pets.cat{} # Error: Invalid access of internal class constructorCat sınıfının oluşturucusunu pets modülünün dışında çağırmak başarısız olur çünkü class anahtar sözcüğü dahili olarak işaretlenir. Bu durum, sınıf tanımlayıcısı genel olarak işaretlenmiş olsa bile geçerlidir. Genel olarak işaretlenmesi, cat sınıfına pets modülünün dışından referans verilebileceği anlamına gelir.
Sınıf anahtar sözcüğüyle birlikte kullanabileceğin tüm erişim belirleyicilerini aşağıda görebilirsin:
Public: Sınırsız erişim. Varsayılan erişim budur.
Internal: Erişim geçerli modülle sınırlıdır.
Concrete Belirleyicisi
Bir sınıf concrete (somut) belirleyicisine sahip olduğunda, bu sınıfı cat{} gibi boş bir arketip ile oluşturmak mümkündür. Buna göre, sınıfın her alanının varsayılan bir değere sahip olması gerekir. Dahası, somut bir sınıfın her alt sınıfının da somut olması gerekir.
Örneğin:
class1 := class<concrete>:
Property : int = 0
# Error: Property isn't initialized
class2 := class<concrete>:
Property : int
# Error: class3 must also have the <concrete> specifier since it inherits from class1
class3 := class(class1):
Property : int = 0Bir concrete sınıfının bir soyut sınıftan doğrudan devralabilmesi için her iki sınıfın da aynı modülde tanımlanmış olması gerekir. Bununla birlikte, bu geçişli olarak tutmaz. Bir concrete sınıfı başka bir modüldeki ikinci bir concrete sınıfından, söz konusu ikinci concrete sınıfının kendi modülündeki abstract (soyut) bir sınıftan doğrudan devraldığı durumda doğrudan devralabilir.
Unique Belirleyicisi
Unique (benzersiz) belirleyicisi, bir sınıfı benzersiz bir sınıf yapmak için o sınıfa uygulanabilir. Verse, benzersiz bir sınıfın bir örneğini oluşturmak için ortaya çıkan örneğe benzersiz bir kimlik ayırır. Bu işlem, benzersiz sınıf örneklerinin, kimlikleri karşılaştırılmak suretiyle eşitlik açısından karşılaştırılmasına imkan tanır. Unique belirleyicisi olmayan sınıfların böyle bir kimliği yoktur, dolayısıyla bunlar eşitlik açısından yalnızca alanlarının değerlerine göre karşılaştırılabilir.
Buna göre, benzersiz sınıflar = ve <> işleçleriyle karşılaştırılabilir, ayrıca karşılaştırılabilir türün alt türleridir.
Örneğin:
unique_class := class<unique>:
Field : int
Main()<decides> : void =
X := unique_class{Field := 1}
X = X # X is equal to itself
Y := unique_class{Field := 1}
X <> Y # X and Y are unique and therefore not equalFinal Belirleyicisi
Final belirleyicisini yalnızca sınıflarda ve sınıf alanlarında kullanabilirsin.
Bir sınıf, final belirleyicisine sahip olduğunda, sınıfın bir alt sınıfını oluşturamazsın. Aşağıdaki örnekte pet sınıfını bir üst sınıf olarak kullanamazsın çünkü sınıf final belirleyicisine sahiptir.
pet := class<final>():
…
cat := class(pet): # Error: cannot subclass a “final” class
…Bir alan final belirleyicisine sahip olduğunda, alanı bir alt sınıfta geçersiz kılamazsın. Aşağıdaki örnekte kedi sınıfı Owner (sahip) alanını geçersiz kılamaz çünkü alan final belirleyicisine sahiptir.
pet := class():
Owner<final> : string = “Andy”
cat := class(pet):
Owner<override> : string = “Sid” # Error: cannot override “final” fieldBir yöntem `final` belirleyicisine sahip olduğunda yöntemi bir alt sınıfta geçersiz kılamazsın. Aşağıdaki örnekte kedi sınıfı GetName() metodunu geçersiz kılamaz çünkü metot final belirleyicisine sahiptir.
pet := class():
Name : string
GetName<final>() : string = Name
cat := class(pet):
…
GetName<override>() : string = # Error: cannot override “final” method
…Sınıf Gövdesinde Block İfadeleri
Block ifadelerini bir sınıf gövdesinde kullanabilirsin. Sınıfın bir örneğini oluşturduğunda, block ifadeleri tanımlandıkları sıraya göre yürütülür. Sınıf gövdesindeki block ifadelerinde çağrılan fonksiyonlar, NoRollback efektine sahip olamaz.
Örnek olarak, cat sınıf gövdesine iki block ifadesi ekleyelim, Meow() metoduna da transacts efekti belirleyicisini ekleyelim, çünkü metotların varsayılan efekti NoRollback efektine sahiptir.
cat := class():
Name : string
Age : int
Sound : string
Meow()<transacts> : void =
DisplayOnScreen(Sound)
block:
Self.Meow()
Cat sınıfının örneği olan OldCat oluşturulduğunda, iki block ifadesi yürütülür: Kedi önce “Rrrr” diyecektir, ardından çıktı günlüğüne “Garfield” yazdırılacaktır.
Arayüzler
Arayüzler, yalnızca bir değeri olmayan metotları içerebilen sınırlı bir sınıf biçimidir. Sınıflar yalnızca diğer tek bir sınıftan devralma yapabilir, ancak herhangi bir sayıda arayüzden devralma yapabilir.
Kalıcı Tür
Bir sınıf aşağıdaki durumlarda kalıcıdır:
Kalıcı belirleyici ile tanımlanır.
Final belirleyicisi ile tanımlanır çünkü kalıcı sınıflarda alt sınıflar olamaz.
Benzersiz değil.
Bir üst sınıfı yok.
Parametrik değil.
Yalnızca aynı zamanda kalıcı olan üyeleri içerir.
Değişken üyeleri yok.
Bir sınıfın kalıcı olması, bunları modül kapsamındaki weak_map değişkenlerinde kullanabileceğin ve değerlerini oyun oturumları arasında kalıcı olmasını sağlayabileceğin anlamına gelir. Verse’te süreklilik hakkında daha ayrıntılı bilgi için Verse’te Kalıcı Veri Kullanımı bölümüne bakabilirsin.
Aşağıdaki Verse örneği, bir oyuncu için depolanabilecek, güncellenebilecek ve daha sonra erişilebilecek özel bir oyuncu profilini bir sınıfta nasıl tanımlayabileceğini gösterir. Player_profile_data sınıfı, bir oyuncunun kazandığı TP, sıralaması ve tamamladığı görevler gibi bilgileri depolar.
player_profile_data := class<final><persistable>:
Version:int = 1
Class:player_class = player_class.Villager
XP:int = 0
Rank:int = 0
CompletedQuestCount:int = 0
QuestHistory:[]string = array{}