GTAMulti - GTA Türkiye

San Andreas Multiplayer => PAWN Kodlama => Mod Galerisi => Konuyu başlatan: Amper - 08 Aralık 2020, 20:49:57

Başlık: [PAWN] Nesne Yönelimli Programlama(aslında değil) (Southclaws)
Gönderen: Amper - 08 Aralık 2020, 20:49:57
Nesne Yönelimli Programlama(OOP, Object Oriented Programming)
Başlamadan önce bu gerçek bir OOP kodlama stili değildir. Sadece Pawn üzerinde kullanmanız gereken ve düzen açısından en iyilerinden biri olarak sayılabilecek kodlama stilidir.

Typescript ile ilk kod yazmaya başladığım zamanlar artık bir zorunluluk haline getirdiğim bu kodlama stiliyle Pawn üzerinde devam etmek istedim fakat haliyle çok mümkün olmadı. Bu yüzden samp.js'i kullanarak tip desteğini dahil edip Typescript ile SA-MP üzerinde bir süre kod yazdım ve easydialog(kullanmayın, kullandırtmayın)'ı son sürüm haliyle Typescript'de kullanılabilecek bir düzen yazdım(https://github.com/lilAmper/SA-MP-TypeScriptDeathmatch) garip bir şekilde yabancılara karşı hiçbir paylaşım yapmasam bile sadece github üzerinden bile iki adet fork aldı ve bence bu mükemmel bir şey.

Asıl konuya gelecek olursak Pawn üzerinde OOP kodlama stilini uygulayabilmek için bir arayışa girdim ve Southclaws bana kendisinin birkaç sene önce yazdığı bu düzeni attı(bu gerçek bir OOP kodlama stili değil).

Zaten normal şartlarda bu şekilde kod yazdığım dönemler oldu fakat her zaman aynı düzeni koruyamadım. Uzun zamanda bu düzeni kullanarak devam edildiğinde çok daha rahat bir işleyişe kavuştum diyebilirim.

/*
Southclaw's "Object Oriented" modular coding style for Pawn. This documents
the basics of structuring a create/destroy type entity. This method can be
used for almost any kind of "thing" that can be created, destroyed and
modified within its life cycle.
*/
#include <YSI\y_hooks>


/*
We're using the generic term "entity" in this example. This could be
anything, a SIF/Button, an object, a vehicle, a pickup, a combination of all
of those!
*/
#define MAX_ENTITY (64)


/*
Enumerator contains the structure for each entity's "instance". Think of
this as a class or struct in OO languages. Nothing to do with functions or
"methods" though, just a storage structure for data associated with each
entity.
*/
enum E_ENTITY_DATA {
Float:ent_posX,
Float:ent_posY,
Float:ent_posZ
}


/*
Static globals are used here since this "module" is designed to be included
into a gamemode or filterscript and we don't want names to conflict. Global
variables declared as static will only be accessible inside the file.
*/
static
// Contains the data for each entity
ent_Data[MAX_ENTITY][E_ENTITY_DATA],
// Contains the index of all entities.
Iterator:ent_Index<MAX_ENTITY>;


/*
The "constructor" function, you'll be familiar with this concept if you've
ever seen an OO language. This function creates entities and returns their
"handle" or "id" number (a value from 0 to MAX_ENTITY (64)).
*/
stock CreateEntity(Float:x, Float:y, Float:z) {
// Get the first free ID using the y_iterate index.
new id = Iter_Free(ent_Index);

// Invalid iterator index? Index is full or something else went wrong.
if(id == ITER_NONE) {
return -1; // return an invalid index
}

// ID is valid, fill in our entity's data fields.
ent_Data[id][ent_posX] = x;
ent_Data[id][ent_posY] = y;
ent_Data[id][ent_posZ] = z;

// Return the ID back to where the function was called, this is important
// so the entity can be addressed in some way. This ID can be used to
// modify the entity or destroy it. If this ID is lost, the entity can't be
// explicitly interacted with or destroyed (possible orphan!).
return id;
}

/*
Our destroy function takes an ID (the same kind of value returned from the
constructor) and frees up the memory again while also making the ID
available for use. It also clears
*/
stock DestroyEntity(entityid) {
// If the entity ID isn't in the index, don't bother running the function.
if(!Iter_Contains(ent_Index, entityid)) {
return 1; // return non-zero, zero means success
}

// Resetting the values isn't always necessary, it's just a habit I have.
ent_Data[entityid][ent_posX] = 0.0;
ent_Data[entityid][ent_posY] = 0.0;
ent_Data[entityid][ent_posZ] = 0.0;

// Free up the slot in the index ready for another entity to take its place.
Iter_Remove(ent_Index, entityid);

return 0; // zero means success
}

/*
Manipulation functions go at the bottom of the file, these are functions
that are designed to get/set the values held within the enum structure.
*/
stock GetEntityPos(entityid, &Float:x, &Float:y, &Float:z) {
// Non-existent entity? Don't bother with the function.
if(!Iter_Contains(ent_Index, entityid)) {
return 1; // non-zero means failure
}

x = ent_Data[entityid][ent_posX];
y = ent_Data[entityid][ent_posY];
z = ent_Data[entityid][ent_posZ];

return 0; // zero means success
}

stock SetEntityPos(entityid, Float:x, Float:y, Float:z) {
// Non-existent entity? Don't bother with the function.
if(!Iter_Contains(ent_Index, entityid)) {
return 1; // non-zero means failure
}

ent_Data[entityid][ent_posX] = x;
ent_Data[entityid][ent_posY] = y;
ent_Data[entityid][ent_posZ] = z;

return 0; // zero means success
}
Başlık: Ynt: [PAWN] Nesne Yönelimli Programlama(aslında değil) (Southclaws)
Gönderen: squarepants - 09 Aralık 2020, 00:37:33
Herhangi bir sınıftan nesne türetmek, sıradan enumator ve Iterator yapıları ile de sağlanılabilir. Temelde amaç birden fazla özelliği farklı nesneler ile aynı sınıf altında kullanabilmektir. Örnek vermek gerekir ise :

#define MAX_NESNEM  (500)
#define MAX_SINIF    (100)

enum Sinif_A
{
 Ad[MAX_PLAYER_NAME],
 Yas,
 bool:Cinsiyet
}
new
 Iterator:Sinifim[MAX_SINIF]<MAX_NESNEM>,
 Objem[MAX_NESNEM][Sinif_A]
;
OnGameModeInit()
{
    Iter_Init(Sinifim);
    return 1;
}
Bu sayede türetilecek olan nesneleri aynı veya farklı sınıf altında oluşturabilir, ve belirtilen özellikleri atayabiliriz. Temelde amaç, Iterator yardımı ile atanılan Sınıf kimliğine bağlı olarak dahil edilen sınıfa ait boş bir Index (Nesne Kimliği) çekebilmektir. Bu sayede Oluşturulan nesneler dizi için kullanıldıklarında çakışma yaşamazlar.

stock Iter_Index_Cek(Sinif_ID)
{
 new Bos_Index = Iter_Free(Sinifim[Sinif_ID]);
 static Index;
 Iter_Add(Sinifim[Sinif_ID],Index);
 Index++;
 return Bos_Index;
}
Görüldüğü üzere, Girilen sınıf kimliğine bağlı olarak Boş bir Index (Nesne) Kimliği elde edilir. Daha sonra ise elde edilen bu nesne kimliği, belirtilen sınıf kimliği adı altında kullanılabilir.

Örnek Uygulama :
main()
{
    new test1 = Iter_Index_Cek(0);
    new test2 = Iter_Index_Cek(0);

    Objem[test1][Cinsiyet] = false;
    Objem[test1][Yas] = 20;
    Objem[test2][Cinsiyet] = true;
    Objem[test2][Yas] = 21;

    printf("test1 Yas %i Cinsiyet %i", Objem[test1][Yas], Objem[test1][Cinsiyet])
    printf("test2 Yas %i Cinsiyet %i", Objem[test2][Yas], Objem[test2][Cinsiyet]);
}
Başlık: Ynt: [PAWN] Nesne Yönelimli Programlama(aslında değil) (Southclaws)
Gönderen: Amper - 09 Aralık 2020, 01:28:24
Evet, aynı örnek. @ThePosition

Sadece PAWN kullanarak bu şekilde kod yazılsa bile birçok bilgiyi içeride tutmak çok zorlu olabiliyor. En iyi örneği LSA Roleplay'ı kodlarken sadece bir tablo içerisinde en az 50 sütün vardı ve ilişkili veritabanı olduğu için bir bilgiyi çekeceğim zaman çoğunlukla başka tablolarla iletişim halinde oluyordum ve 50'yi geçkin bir sütün sayısı getiriyordum ve onları yönetmeye çalışıyordum. Gerçekten öldürücü bir olaydı. Bu yöntemi kullanarak yapmamıştım çünkü PAWN kendi içerisinde dynamic data'yı desteklemiyor ve benim tablo içeriklerim çok değişkendi(500 veri barındıran bir tablo bir saat sonra 1000 veri barındırabiliyordu). Buna çözüm olarak şu an pawn-vector(https://github.com/BigETI/pawn-vector) kullanıyorum ve aynı şekilde birçok callback'e bilgi aktardığım için daha performanslı olmasını istiyorum bu yüzden Pointerları da yanında kullanıyorum.

Bunlarla birlikte söylemek gerekirse PAWN içerisinde ne kadar uğraş verip bir düzen oturtsam bile hiçbir zaman yeterli gelmedi. SAMP Sharp veya Typescript ile kod yazmak en mantıklısı gibi duruyor.
Başlık: Ynt: [PAWN] Nesne Yönelimli Programlama(aslında değil) (Southclaws)
Gönderen: Backup - 09 Aralık 2020, 08:34:58
Alıntı yapılan: ThePosition - 09 Aralık 2020, 00:37:33
Herhangi bir sınıftan nesne türetmek, sıradan enumator ve Iterator yapıları ile de sağlanılabilir. Temelde amaç birden fazla özelliği farklı nesneler ile aynı sınıf altında kullanabilmektir. Örnek vermek gerekir ise :

#define MAX_NESNEM  (500)
#define MAX_SINIF    (100)

enum Sinif_A
{
 Ad[MAX_PLAYER_NAME],
 Yas,
 bool:Cinsiyet
}
new
 Iterator:Sinifim[MAX_SINIF]<MAX_NESNEM>,
 Objem[MAX_NESNEM][Sinif_A]
;
OnGameModeInit()
{
    Iter_Init(Sinifim);
    return 1;
}
Bu sayede türetilecek olan nesneleri aynı veya farklı sınıf altında oluşturabilir, ve belirtilen özellikleri atayabiliriz. Temelde amaç, Iterator yardımı ile atanılan Sınıf kimliğine bağlı olarak dahil edilen sınıfa ait boş bir Index (Nesne Kimliği) çekebilmektir. Bu sayede Oluşturulan nesneler dizi için kullanıldıklarında çakışma yaşamazlar.

stock Iter_Index_Cek(Sinif_ID)
{
 new Bos_Index = Iter_Free(Sinifim[Sinif_ID]);
 static Index;
 Iter_Add(Sinifim[Sinif_ID],Index);
 Index++;
 return Bos_Index;
}
Görüldüğü üzere, Girilen sınıf kimliğine bağlı olarak Boş bir Index (Nesne) Kimliği elde edilir. Daha sonra ise elde edilen bu nesne kimliği, belirtilen sınıf kimliği adı altında kullanılabilir.

Örnek Uygulama :
main()
{
    new test1 = Iter_Index_Cek(0);
    new test2 = Iter_Index_Cek(0);

    Objem[test1][Cinsiyet] = false;
    Objem[test1][Yas] = 20;
    Objem[test2][Cinsiyet] = true;
    Objem[test2][Yas] = 21;

    printf("test1 Yas %i Cinsiyet %i", Objem[test1][Yas], Objem[test1][Cinsiyet])
    printf("test2 Yas %i Cinsiyet %i", Objem[test2][Yas], Objem[test2][Cinsiyet]);
}


Nesne üretmek sadece veri yükleyip veri çekmekten ibaret değil. Pawnda yapamayacağınız  Absract classlar, Inheritance, Polymorphism, Interfaceler ve buna bağlı olarak Implementler. Method overloading ve Method Overriding. Private, Protected methodlar/propertyler ayrıca static özelliği gibi zibilyon tane konu var.

Salt PAWN kullanarak OOP yazamazsınız.

Ha amaç sadece değişkenleri enumatorde toplayıp değer atayıp, değer çekmek ise ve bazı stock fonksiyonları kullanıp işlev vermek ise evet yapabilirsiniz. Ancak bu OOP prensiplerini kullandığınız anlamına gelmez.
 
Alıntı yapılan: ThePosition - 09 Aralık 2020, 00:37:33
Herhangi bir sınıftan nesne türetmek, sıradan enumator ve Iterator yapıları ile de sağlanılabilir.

Bu kesinlikle yanlış bir cümle.



Başlık: Ynt: [PAWN] Nesne Yönelimli Programlama(aslında değil) (Southclaws)
Gönderen: squarepants - 09 Aralık 2020, 12:20:29
Bu kesinlikle yanlış bir cümle.
Bu tarz konularda daha önce de tartışmıştık. Yanlış anlıyorsun, ya da öyle anlamak istiyorsun. Bu arada kısmen kelimesi kabul edilebilir, ancak kesinlikle kelimesini kabul edemem. Yukarıdaki örnekte de gördüğün üzere temelde amaç, OOP yapısının oluşturduğu prensibin bire bir aynısını oluşturmak değil, varsayılan prensibe en yakın ve doğru olan algoritmayı kullanmak. Örneği detaylı bir şekilde incelediysen kısmen de olsa Enum ve Iter yapısını kullanarak varsayılan OOP algoritmasına benzetmek. Daha iyi bir fikrin var ise konuda olumsuz yargı dağıtmak yerine var olan o fikrini paylaşırsan en azından tüm Platform için bir yararın dokunmuş olur.

Nesne üretmek sadece veri yükleyip veri çekmekten ibaret değil.
 Ancak bu OOP prensiplerini kullandığınız anlamına gelmez.
Keşke şu cümlelere ait herhangi bir yorum yapsaydım da öyle yazsaydın. Amacının ne olduğunu bilmiyorum, ama devam et.
Başlık: Ynt: [PAWN] Nesne Yönelimli Programlama(aslında değil) (Southclaws)
Gönderen: Backup - 09 Aralık 2020, 13:32:37
Alıntı yapılan: ThePosition - 09 Aralık 2020, 12:20:29
Bu kesinlikle yanlış bir cümle.
Bu tarz konularda daha önce de tartışmıştık. Yanlış anlıyorsun, ya da öyle anlamak istiyorsun. Bu arada kısmen kelimesi kabul edilebilir, ancak kesinlikle kelimesini kabul edemem. Yukarıdaki örnekte de gördüğün üzere temelde amaç, OOP yapısının oluşturduğu prensibin bire bir aynısını oluşturmak değil, varsayılan prensibe en yakın ve doğru olan algoritmayı kullanmak. Örneği detaylı bir şekilde incelediysen kısmen de olsa Enum ve Iter yapısını kullanarak varsayılan OOP algoritmasına benzetmek. Daha iyi bir fikrin var ise konuda olumsuz yargı dağıtmak yerine var olan o fikrini paylaşırsan en azından tüm Platform için bir yararın dokunmuş olur.

Nesne üretmek sadece veri yükleyip veri çekmekten ibaret değil.
 Ancak bu OOP prensiplerini kullandığınız anlamına gelmez.
Keşke şu cümlelere ait herhangi bir yorum yapsaydım da öyle yazsaydın. Amacının ne olduğunu bilmiyorum, ama devam et.


Sana karşı bir garezim yok. Amacım platformun doğru bilgi ışığında yönelmesini sağlamak. Bu ışığı parıldatmam için yanlış bilgilerin önüne geçmem gerekiyor.

Evet Enumator pawn için en iyi örnek bunu kabul ediyorum ve platform olarak verdiğin örnek için de teşekkür ediyoruz ancak verdiğin örnek, OOP'nin temeline göz kırpsa da OOP ile aynı nitelikte olduğunu söyleyemeyiz.

Terimlerin olabildiğince doğru kullanılması taraftarıyım.
Başlık: Ynt: [PAWN] Nesne Yönelimli Programlama(aslında değil) (Southclaws)
Gönderen: Amper - 09 Aralık 2020, 16:30:18
@Backup Gerçek OOP prensiplerini tamamen karşılayacak bir düzen kurmak PAWN içerisinde evet imkansız fakat zaten konu içerisinde, başlıkta ve her yerde belirtmek istediğim şey bu. En yakın düzeyde kullanılabilecek düzenin bu olduğunu açıkladım ve ayrıca başka bir kodlama dilinden SA-MP'a Pawn kodlamak için gelen bir insanın genellikle nasıl bir düzen içerisinde kod yazacağını göstermek için bu konuyu açtım. İkinizin arasındaki tartışmanın devam etmesinin bir mantığı yok ve ikinize de yukarıdaki amaçlarıma yardımcı olduğunuz için teşekkür ediyorum.