Forum: Ders Arası RSS
MVC -> D <- SDL'nin Buluştuğu Nokta
Sayfa:  1  2  sonraki 
Avatar
Salih Dinçer #1
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Konu adı: MVC -> D <- SDL'nin Buluştuğu Nokta
Merhaba,

Bu hafta bir MVC olayını öğrenelim, ne dersiniz? Lütfen herkes katılım sağlasın; fikir katkısı olsa bile. Böylece hepimiz daha çok şey öğrenip bilginin büyümesine şahit olacağız...:)

Geçen hafta şurada değindiğimiz için bir kaç küçük hazırlık yaptım. Bugüne kadar D'de MVC denemesi yapamıyordum. Çünkü SDL'yi tanımamıştım ama şimdi istediğimiz amaç (Model) doğrultusunda çevre birimleri dahil (Control) ederek işlem sonucunu ekrana (View) verebileceğiz. Bunun için aşağıdaki gibi basit bir örnek hazırladım:
import std.math, sdl;
 
   immutable sahneGenişliği   = 640;
   immutable sahneYüksekliği  = 480;
   immutable çerçeveKalınlığı = 40;
   immutable yuvarlakÇapı     = 21;
 
int main(){
   scope (exit) { // sahneBitir
      SDL_Quit();
      printf("-YAZILIM HATASIZ SONLANDI-\n");
      printf("Hadi yine iyisin...:)\n");
   }
   // Yuvarlağın konumu
   double nXkonumu = sahneGenişliği /2;
   double nYkonumu = sahneYüksekliği/2;
 
   // Yuvarlağın ivmesi
   double nXivmesi = 0;
   double nYivmesi = 0;
 
   // sahneKur
   SDL_Surface* sahne = SDL_SetVideoMode(sahneGenişliği, sahneYüksekliği, 0, 0);
   int beyaz = SDL_MapRGB(sahne.format, 0xff, 0xff, 0xff);
   SDL_Rect rect;// = new SDL_Rect();
   rect.w = cast(ushort)(sahneGenişliği - çerçeveKalınlığı);
   rect.h = cast(ushort)(sahneYüksekliği - çerçeveKalınlığı);
   rect.x = cast(short)(sahne.w-rect.w)/2;
   rect.y = cast(short)(sahne.h-rect.h)/2;
   SDL_FillRect(sahne, &rect, beyaz);
   SDL_UpdateRect(sahne,0,0,sahne.w,sahne.h); 
 
   SDL_Event event;
   bool solTuş, sağTuş, üstTuş, altTuş;
   // sonsuz döngü
   while (true) {
      // nesneÇiz
      if(circleFill(sahne, cast(int)nXkonumu,
                           cast(int)nYkonumu, yuvarlakÇapı, 0)) {
         SDL_Flip(sahne);
      }
      while (SDL_PollEvent(&event)) {
         if(event.type == SDL_KEYDOWN) {
            switch(event.key.keysym.sym) {
               case SDLK_LEFT   : solTuş = true; break;
               case SDLK_RIGHT  : sağTuş = true; break;
               case SDLK_UP     : üstTuş = true; break;
               case SDLK_DOWN   : altTuş = true; break;
               default          : break;
            }
         } else if(event.type == SDL_KEYUP) {                    
            switch(event.key.keysym.sym) {
               case SDLK_ESCAPE : return 0;
               case SDLK_LEFT   : solTuş = false; break;
               case SDLK_RIGHT  : sağTuş = false; break;
               case SDLK_UP     : üstTuş = false; break;
               case SDLK_DOWN   : altTuş = false; break;
               default          : break;
            }
         } else if(event.type == SDL_QUIT) return 0;
      } // tuş döngüsü
   } // sonsuz döngü
} // program sonu
 
bool circleFill(SDL_Surface* gScreen, int x, int y, int r, int c) {
  for (int i = 0; i < 2 * r; i++) {
    if ((y - r + i) >= 0 && (y - r + i) < sahneYüksekliği) {
      int len = cast(int)sqrt(cast(float)(r * r - (r - i) * (r - i))) * 2;
      int xofs = x - len / 2;
      if (xofs < 0) {
        len += xofs;
        xofs = 0;
      }
      if (xofs + len >= sahneGenişliği) {
        len -= (xofs + len) - sahneGenişliği;
      }
      int pitch = gScreen.pitch / 4;
      int ofs = (y - r + i) * pitch + xofs;
      for (int j = 0; j < len; j++) {
        (cast(uint*)gScreen.pixels)[ofs + j] = c;
      }
    } // end if()
  } // end for()
  return true;
}
Tabi burada MVC diye bir şey yok ve düz mantıkla çalışıyor. Yani yuvarlak çizme işlevini (circleFill) saymazsak her şey iç içe girmiş. Amacımız ise ekran ortasındaki topu, yön tuşlarını kullanarak duvara çarptırmak ve aksetmesini gözlemlemek. Gerçi yukarıdaki kod bunu yapmıyor ama hele bir MVC yapımızı oluşturalım devam edeceğiz.

Bu arada Windows kullanıcıları çok şanslılar! DMD hariç bir şey kurmalarına gerek yok. Sadece derlerken şuradaki sembol dosyasına, çalıştırırken ise şu iki dosyaya ihtiyaçları var:

Başarılar...
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
Bu mesaj 2 defa değişti; son değiştiren: Salih Dinçer; zaman: 2012-08-13, 13:38.
Değişiklik nedeni: Başlığı ve bir iki şeyi düzelttim...
acehreli (Moderatör) #2
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı

Yine ilgisizce ve gereksizce karışıyorum: .lib dosyasına kütüphane demek daha doğru. Sembol dosyalarında yalnızca hata ayıklarken yararlanılan isimler bulunur.

Ali
Avatar
Salih Dinçer #3
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Yanıtlanan mesaj #1
Teşekkürler hocam, işte bu da bir katkı! Öyle ya, tarih boyu insanoğlu sürekli hata yaptı...:)

Örneğin bir yanlış daha, başlığı "...Buluştuğu Nokta" şeklinde bitirmem gerekirken yanlış yazmışım...

Dosya uzantı konusunda haklısın çünkü LIB, library yani kütüphanenin kısaltılmış. Ama zannedersem bunlar Common Object File Format (COFF) dosyası. Linker ile MS'un kütüphanesine (DLL: dynamic-link library) bağlanıyor. Hep bunlar kafa karıştırıcı ki bir gün D'nin kütüphane olayı ile bir Ders Arası yapalım mı? Bunu da öğrenmeliyiz!
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
acehreli (Moderatör) #4
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Salih Dinçer:
bunlar kafa karıştırıcı

Ben de gerekenden fazlasını bilmiyorum.

Windows ortamındaki kütüphane çeşitleriyle ilgili sorunlar tarihe karışmak üzere. Walter Win64 programı desteğini tamamlamaya çalışıyor:

  http://forum.dlang.org/thread/k054e9$1si6$1@digitalmars.com

Özellikle şu yanıtına bakılırsa OMF'ye olan eski bağımlılık ortadan kalkıyormuş:

  http://forum.dlang.org/thread/k054e9$1si6$1@digi…?page=5…

Ali
erdem (Moderatör) #5
Üye Tem 2009 tarihinden beri · 981 mesaj · Konum: Eskişehir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Salih Dinçer:
Ama zannedersem bunlar Common Object File Format (COFF) dosyası.

Salihcim bu mesajda bu konuyu biraz anlatmıştım. Yalnız orada ufak bir yazım hatası yapmışım. Microsoft Visual Studio ile gelen derleyici COFF kullanıyor.

http://ddili.org/forum/post/6643

Daha ayrıntılı olarak senin de paylaştığın bu makalede anlatılıyor.

http://www.gamedev.net/blog/1140/entry-2254003-binding-d-t…

implib aracıyla var olan DLL dosyasından lib dosyası oluşturabilirsin. Örneğin

implib -a inpout32.lib inpout32.dll

şeklinde ya da COFF biçiminden OMF biçimine çevirebilirsin. Bunun için de Digitalmars'ın coffimplib aracını kullanabilirsin ancak bu ücretsiz değil. Borland'ın ücretsiz coff2omf aracını da kullanabilirsin. Ya da kaynak kodu Borland derleyicileri ile derleyebilirsin. Bundan da şu adreste bahsetmiştim:

http://stackoverflow.com/questions/11421835/correct-way-of…

acehreli:
Özellikle şu yanıtına bakılırsa OMF'ye olan eski bağımlılık ortadan kalkıyormuş:

  http://forum.dlang.org/thread/k054e9$1si6$1@digi…?page=5…

Bu da güzel bir haber!
Avatar
Salih Dinçer #6
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Yanıtlanan mesaj #4
Şimdi, üç "module" dosyasımız var diyelim, bunlar:
  • model.d
  • view.d
  • control.d

Elbette bunların isimleri farklı da olabilir ama MVC olayını tam manasıyla hissedebilmek için şimdilik isimleri böyle olsun. Bu durumda, biz bu üç "module" dosyasını şöyle derleriz:

dmd sdltest sdl model view control sdl.lib

(Derlenen ilk dosya içinde main() var, ikincisinde ise SDL ilintileri...)

Peki, tıpkı ilk iletimde bahsettim gibi control.d içinde tuşlardaki hareketleri algılayan bir döngümüz olacak. Ancak burada 4 değişken (bool solTuş, sağTuş, üstTuş, altTuş;) model.d dosyası ile haberleşmeli. Aklıma TUŞLAR isminde bir struct oluşturmak ve bahsettiğim değişkenleri kontrolEt() işlevi ile yapılaştırmak geliyor.

Sizce uygun mudur?
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
acehreli (Moderatör) #7
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Fazla incelemiş gibi olmayayım ama "tuş" dediğin an sorumlulukları karıştırmaya başlıyorsun. Bence arayüzde "yön" kavramı bulunmalı. Bu kavram tuşlardan da edinilebilir, fareden de, başka bir şeyden de.

Ali
erdem (Moderatör) #8
Üye Tem 2009 tarihinden beri · 981 mesaj · Konum: Eskişehir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Bence de birden fazla kaynak dosyası da olabilir.

[Resim: http://upload.wikimedia.org/wikipedia/commons/f/fd/MVC-Process.png]

Örneğin kullanıcı denetim birimini kullanıyor. Burada model derken oyuncu ya da diğer oyun karakterlerini kasdediyoruz. Gerçekten de aslında bunlar sadece bazı veriler içeriyor. Denetim birimi de bu veriler üzerinde değişiklik yapıyor. Model üzerinde meydana gelen değişiklikleri çizici sınıf ekrana çiziyor. Sonra kullanıcı da bu son değişikleri görmüş oluyor.

İşte bir örnek:

[Resim: http://www.nuclex.org/images/stories/blog/gamedev/islandwar/islandwar-mvc-design.png]

Bu konuda Programming Game AI By Example kitabında sanırım örnekler vardı.

http://amzn.com/1556220782
Avatar
Salih Dinçer #9
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
İlk denememi bitirdim, merak edenler buradan deneyebilir: https://github.com/salihdb/MVC

Alıştığımız tek dosyalık örneği +3 parçaya böldüm. Gerçi toplamda 35-45 satırlık 4 dosya ve 156 satır meydana geldi yani kod hacmi büyüdü!

Sanki MVC'nin çok büyük bir esprisi yok gibi. Çünkü bu hale getirmek bana en az 3 kat vakit kaybı getirdi. Üstelik tek dosyanın serbestliği yanında pek tercih edilesi görünmüyor. Hatta control.d içindeki yazılımın sonlanma hadisesini taaa main() içine taşıyabilmek için epey cebelleştim...:)

Bu MVC ne menem bir şeymiş! Özetle 150 KB.'ı geçmeyen Win32 uygulamam geçer oldu. Belki aşağıdaki main()'nin satırı sayısı 3'de 1'e indi ama toplamda 1/3 kat arttı:
import core.stdc.stdio: printf;
import sdl, model, view;
 
int main(){
   ÇİZ çizmeye;   // model.d
   SAHNE sahne;   // view.d
   //TUŞLAR tuş;    // control.d
   
   scope(failure) {
      printf("-YAZILIM HATALI SONLANDI-\n");
      printf("=========================\n");
      printf("Hata bilgileri:\n");
   }
 
   if(SDL_Init(SDL_INIT_VIDEO) < 0) {
      throw new Exception("HATA: Uygun olmayan vidyo!");
   }
   
   // sahneKur
   SDL_Surface* ekran = SDL_SetVideoMode(sahne.genişliği,sahne.yüksekliği,0,0);
   
   if(ekran == null) {
      throw new Exception("HATA: Uygun olmayan sahne!");
   }
   scope (exit) { // sahneBitir
      SDL_Quit();
      printf("-YAZILIM HATASIZ SONLANDI-\n");
      printf("Hadi yine iyisin...:)\n");
   }
   int beyaz = SDL_MapRGB(ekran.format, 0xff, 0xff, 0xff);
 
   SDL_Rect çerçeve;
            çerçeve.w = cast(ushort)(sahne.genişliği - sahne.çerçeveKalınlığı);
            çerçeve.h = cast(ushort)(sahne.yüksekliği - sahne.çerçeveKalınlığı);
            çerçeve.x = cast(short)(ekran.w - çerçeve.w)/2;
            çerçeve.y = cast(short)(ekran.h - çerçeve.h)/2;
   SDL_FillRect(ekran, &çerçeve, beyaz);
   SDL_UpdateRect(ekran, 0, 0, ekran.w, ekran.h); 
 
   return çizmeye.başla(ekran, &çerçeve)// sonsuz döngü
} // program sonu 
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
acehreli (Moderatör) #10
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Salih Dinçer:
Sanki MVC'nin çok büyük bir esprisi yok gibi.

MVC ile ilgili böyle görüşlere hiç rastlamamıştım. Belki daha yararını anlayamadık. Hangi kaynağı okumamı önerirsin?

Ali
erdem (Moderatör) #11
Üye Tem 2009 tarihinden beri · 981 mesaj · Konum: Eskişehir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Yanıtlanan mesaj #9
Sanırım model.d'nin içindeki çizim kodları olmaması gerekiyor. Çizim ile kodlar çizicinin view içinde oluyor.
Avatar
Salih Dinçer #12
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Yanıtlanan mesaj #10
Merhaba,

Geçen zaman içinde MVC hakkında, gereksizden öte saçma sapan bir şey olarak görmeye başladım! Çünkü programcıyı, kalıplaşmış bir yapı içinde sıkıştırıyor ve onu üç kategoriye göre seçim yapma zorunluluğunda bırakıyor. Bu ise zaman kaybı, stres ve kodu kalıba uygunlaştırma çabalarını beraberinde getiriyor.

Örneğin bu kodu bugün SDL2 ile tasarladım. Kodu yine parçalara böldüm ama kendimi zorlamadığım gibi kodu geliştirmeye başladım. Toplamda 4 dosyam oldu:
  • oyun.d
  • pencere.d
  • nesne.d
  • olaylar.d

Olaylar modülünde, şimdilik sadece klavye tuşlarını kontrol eden bir yapı var; bu önceki denememdeki control.d ile hemen hemen eşdeğer.

Pencere ve Nesne modülleri ise halen geliştirmeye açık iki yeni sınıf; içeriği model&view karışımı bir şey...

İlk dosya, bütün bunları çağıran main() işlevinin ve while() döngüsünün yer aldığı bölüm; içinde model de var, control de, view de...:)

Ali Çehreli:
Salih Dinçer:
Sanki MVC'nin çok büyük bir esprisi yok gibi.

MVC ile ilgili böyle görüşlere hiç rastlamamıştım. Belki daha yararını anlayamadık. Hangi kaynağı okumamı önerirsin?
Önerebileyeceğim tek kaynak: İnsan Psikolojisi...:)
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
erdem (Moderatör) #13
Üye Tem 2009 tarihinden beri · 981 mesaj · Konum: Eskişehir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Salihcim bu konuda seninle aynı fikirde değilim. MVC tasarım deseni oyunlarda kullanılıyor.

http://www.loria.fr/~quinson/Teaching/CSH/TP-09.pdf

Ama maalesef sanırım sen benim daha önce yaptığım hataları yapıyorsun. Ben de ilk zamanlarda oyunu nasıl daha güzel kodlayabilirim, nasıl iyileştirmeler yapabilirim hatta senin de bahsettiğin tasarım desenlerini kullanabilirim diye düşünmüştüm. Aslında bu profesyonel oyun programcılarının bile yaptığı bir hataymış. Yani bazen öyle bir şey oluyor ki oyun kodlamak için kod yazmaya başlıyorsun. Sonra bir bakıyorsun 2D oyun motoru kodlamışsın!  :-) Ama ortada oyun yok.

Hatta bu SDL projesinde bile ilk planda benim niyetim Ali beyin yazmış olduğu Pişti oyunu için grafiksel bir arabirim yazmaktı. Sonra bir baktım kendimi 2D grafik kütüphanesi kodlarken buldum.

Demek istediğim biraz çorba kod olsa da, ilk önceliğin kendine bir hedef belirleyip en kısa zamanda onu kodlamak olmalı. Örneğin bir Pacman olabilir, Tuğla Kırma olabilir vs.. Daha sonra oyunun nasıl programlanacağını öğrendiğin zaman artık hem istediğin programlama tekniklerini (sınıf tasarımını değiştirmek, tasarım desenlerini kullanmak) vs.. uygulayabilirsin hem de oyunu istediğin gibi değiştirebilirsin. Örneğin Pacman oyunu basit bir oyun gibi gözükür değil mi. Ama işin içine yapay zeka girerse hiç de kodlanması basit bir oyun olmadığı görülebilir.

Anlatmak istediğim çok iyi bir programcı olabilirsin. Ama programlama ile oyun programlama farklı şeyler.

Örneğin Gamemaker

http://www.yoyogames.com/make

Scirra gibi

http://www.scirra.com

oyun geliştirme ortamları var. Bunlarda programlama bilmeden oyun yapabiliyorsun. Bazı kişiler bunları önemsiz gibi görseler de oyunların çalışma mantığını anlamak açısından bence önemli.
Avatar
Salih Dinçer #14
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
erdem:
Anlatmak istediğim çok iyi bir programcı olabilirsin. Ama programlama ile oyun programlama farklı şeyler.
Katılyorum...

Oyun programlama konusunda uzman olmak istemediğim gibi bunun zor olduğunu da düşünüyorum. Bunu daha önce "kırk fırın ekmek yemek" sözüyle betimlemeye çalışmıştım. Benim eleştirdiğim ve belki de anlamaya çalıştığım şeyler kalıplar...

Biliyorsunuz, herkesin ortak noktası bilgisayarlar. Hiç ilgili olmayan biri bile (bu çocuk olabilir) oyun sektörü içinde aynı noktada bizlerle buluşuyor. Ancak biz geliştirenler, ActionScript dahil bir çok farklı platform ve programlama dili tercih edebiliyorlar. Her bir programcının da farklı bir deseni olabiliyor. Kabaca söylediğim bütün bu değişkenleri birbirleriyle çarptığımızda bile çok fazla olasılık çıkıyor. Hatta olasılıkları veya programlama dillerini birlikte kullananlar da var...

Bu arada, Erdem'in yukarıda alıntıladığım sözüne bir ekleme yapmak aklıma geldi. Çok iyi oyun programcısı olabiliriz ama iyi bir oyun yazamayız. Çünkü oyun yazmak kollektif bir iş. Arabiriminden tutun da müzikler, en küçük grafikler bile bir sanat, bir uzmanlıktır. Dolayısıyla o sektörde bir iddiam yok ama baldan bir parmak çalıp bir şeyler kapma niyetim var. MVC konusu ne zamandır kafamdaydı ve onun sayesinde bana göre olmadığını anladım.

Hepsi bu...:)
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
acehreli (Moderatör) #15
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Salih Dinçer:
Benim eleştirdiğim ve belki de anlamaya çalıştığım şeyler kalıplar.

Bunu kırıcı olmadan nasıl söyleyeceğimden emin değilim ama "eleştirme" ve "anlamaya çalışma" kavramlarını aynı cümlede kullanıyorsun. :) MVC'nin büyük bir esprisi olmadığını da bu konunun 9 numaralı mesajında söylemiştin. "MVC olayını öğrenelim" dedikten sonra daha sekiz saat bile geçmeden... :) Bence biraz yavaşlayalım ve önce olayı tam olarak anlayalım ve hatta biraz deneyim kazanalım; ondan sonra fikirlerimizi tartalım.

Ben MVC kalıbını uygulayanların körlemesine uyguladıklarını düşünmüyorum. Mutlaka bir yararı vardır. Aslında yararları tanımında açıkça geçiyor: Programın farklı işler yapan bölümlerini birbirlerinden ayırmak. Örneğin program mantığı verinin nasıl görüntülendiğini bilmezse ona lego parçası gibi farklı görüntüleyiciler bağlayabiliyoruz.

programcıyı, kalıplaşmış bir yapı içinde sıkıştırıyor ve onu üç kategoriye göre seçim yapma zorunluluğunda bırakıyor.

O MVC'ye tamamen ters bir görüş. MVC'nin getirdiği yarar sıkıştırmak değil, tam tersine serbest bırakmaktır.

Ayrıca "kalıp" kavramıyla ilgili bir konuyu hatırlatmak istiyorum. Pattern denen bu kalıplar hiçbir zaman birileri oturup daha iyi olduğuna karar verdikleri için ortaya çıkmıyor. Bunlar başka insanların birbirlerinden bağımsız olarak keşfettikleri, yararlarını gördükleri, ve en sonunda da isimlendirdikleri olgulardır. Bir anlamda kalıplar kendiliklerinden ortaya çıkarlar.

MVC konusu ne zamandır kafamdaydı ve onun sayesinde bana göre olmadığını anladım.

Haklı olabilirsin ama ben haddim olmayarak henüz iyi bir uygulamasıyla karşılaşmamış olduğun için öyle düşündüğünü düşünüyorum.

MVC için bir deneme de ben yapacağım. Hatırlayalım:

  • Model, programın asıl işini yürütüyor ve programdaki bilgilerin sahibi.

  • View, programın durumunun dışa vurumunu üstleniyor.

  • Controller, programın kullanıcısıyla etkileşimini hallediyor ve Model'ın değişmesini sağlıyor.

Örnek olarak Salih'in hatırlattığı İşlevler Bölümü'nün ikinci problemini düşünüyorum:

  http://ddili.org/ders/d/islevler.html

Tabii oradaki program MVC ayrımı gözetilmeden yazılmıştır ve zaten kullanıcıyla etkileşim diye bir kaygısı da yoktur.

Model olarak oradan esinlendiğim şu Kağıt modülünden başlayalım:

class Kağıt {
    dchar[][] kareler;          // Kağıdı oluşturan kareler (tuval)
    Kağıtİlgilisi[] ilgililer;  // Bu kağıttaki değişiklerden haberdar olmak
                                // isteyen ilgililer (observers)
 
    this(size_t satırAdedi, size_t sütunAdedi, dchar zemin) {
        auto boşSatır = new dchar[sütunAdedi];
        boşSatır[] = zemin;
 
        foreach (i; 0 .. satırAdedi) {
            this.kareler ~= boşSatır.dup;
        }
    }
 
    // Bu, "observer pattern"da adı geçen registerObserver() işlevidir
    void ilgiliyiTanı(Kağıtİlgilisi ilgili) {
        this.ilgililer ~= ilgili;
    }
 
    // Kağıdı verilen beneklerle doldurur
    void boya(dchar[Yer] benekler) {
        foreach (yer, renk; benekler) {
            kareler[yer.satır][yer.sütun] = renk;
        }
 
        // Yeni benekler oluştuğunu ilgililere haber ver
        foreach (ilgili; ilgililer) {
            // Bu, "observer pattern"daki notify()'dır
            ilgili.değişti(kareler);
        }
    }
}

Üzerinde uzunca konuşmaya değecek bir sınıf değil. Sunduğu iki olanak var:

Birincisi, kağıdın durumunda değişiklik yapmayı sağlayan boya() işlevi. O işlev controller'ın bir kağıt nesnesini değiştirmesi için yeterlidir. Bu işlevin üzerinde durmaya gerek olmadığını düşünebiliriz. Herhalde nesnenin durumunda yapılan değişikliklerin bir üye işlev ile sağlanması gerektiğini artık kanıksamış olmalıyız. (Bunun alternatifi zararlı olabilir: Örneğin kullanıcılar 'noktalar' üyesine doğrudan da erişebilirler ve değiştirebilirlerdi.)

Sunduğu ikinci olanak da kendisine tanıştırılan Kağıtİlgilisi nesnelerini aklında tutması ve kendisinde oluşan değişiklikleri onlara haber vermesi. Bunu, boya()'nın en sonunda yapıyor.

Yukarıdaki Model'ın yararı şu: Bu sınıf yalnızca kendi işini biliyor. Ne kendisini kullananların ayrıntılarını biliyor ne de kendisiyle ilgilenenlerin ayrıntılarını.

Şimdi kağıt nesnelerini kullanmayı bilen bir controller'a bakalım:

class RasgeleBoyayan {
    size_t satırAdedi;
    size_t sütunAdedi;
    size_t rasgeleNoktaAdedi;
 
    this(size_t satırAdedi, size_t sütunAdedi, size_t rasgeleNoktaAdedi) {
        this.satırAdedi = satırAdedi;
        this.sütunAdedi = sütunAdedi;
        this.rasgeleNoktaAdedi = rasgeleNoktaAdedi;
    }
 
    void kullan(Kağıt kağıt) {
        dchar[Yer] noktalar;
 
        foreach (i; 0 .. rasgeleNoktaAdedi) {
            immutable yer = Yer(uniform(0, satırAdedi), uniform(0, sütunAdedi));
            immutable renk= 'a' + uniform(0, 26);
            noktalar[yer] = renk;
        }
 
        kağıt.boya(noktalar);
    }
}

Dışarıdan birisi ona "bu kağıdı kullan" diyor ve o da rasgele renkli noktalar oluşturarak kağıdı o noktalarla boyuyor. Kağıt hakkında tek bildiği, Kağıt.boya işlevi. Bu sınıfın da view nesnelerinden en ufak bir haberi yok. Tek yaptığı, model'da değişiklikler oluşturmak.

Son olarak bir view sınıfına bakalım:

class İkiBoyutluGösterici : Kağıtİlgilisi {
    void değişti(const dchar[][] noktalar) {
        foreach (i, satır; noktalar) {
            writefln("%3s| %s", i, satır);
        }
    }
}

Bu sınıf Kağıtİlgilisi arayüzünü gerçekleştiriyor. İkiBoyutluGösterici.değişti() işlevi Kağıt tarafından "ben değiştim" anlamında çağrılacak.

(Not: Değişiklikler başka biçimlerde de bildirilebilirdi. Ben örnek kısa olsun diye iki boyutlu dchar dizisini olduğu gibi geçirmeye karar verdim.)

Bu view sınıfı da çok yararlıdır: Ne model'dan haberi var ne de controller'dan. Tek bildiği, kendisine verilen değişiklikleri görüntülemek. (Tekrarlamak istiyorum: Bu view sanki kağıdın dchar[][] türündeki üyesinden haberliymiş gibi algılanmasın; kısa olsun diye öyle yaptım. Değişiklikleri bambaşka bir tür olarak da alabilirdi.)

Şimdi bu tasarımın getirdiği esnekliğe bakalım.

main() işlevi model, view, ve controller nesneleri oluşturarak onları birbirlerine tanıştırıyor ve programı işletiyor:

    // Model: Bir kağıt nesnesi oluşturuyoruz
    auto kağıt = new Kağıt(satırAdedi, sütunAdedi, zeminRengi);
 
    // View: İki farklı view nesnesini kağıda tanıtıyoruz
    kağıt.ilgiliyiTanı(new İkiBoyutluGösterici());
    kağıt.ilgiliyiTanı(new ListeleyenGösterici(zeminRengi));
 
    // Controller: Kağıtta değişiklikler yapacak olan nesneyi oluşturuyoruz
    enum rasgeleBenekAdedi = 10;
    auto boyacı = new RasgeleBoyayan(satırAdedi, sütunAdedi, rasgeleBenekAdedi);
 
    boyacı.kullan(kağıt);

O main kullanıldığında model iki farklı ilgili nedeniyle iki farklı biçimde görüntüleniyor:

./mvc_deneme
  0| .....q..q.
  1| .........k
  2| .........h
  3| .r....r...
  4| ...t...yx.

=== Zemin Renginde Olmayan Noktalar ===
0,5: q
0,8: q
1,9: k
2,9: h
3,1: r
3,6: r
4,3: t
4,7: y
4,8: x


Başka view ve controller nesneleri ise tamamen farklı bir program oluşturmaya yetecektir. Aşağıdaki programda iki farklı view ve iki farklı controller var. Bütün programı oluşturan dosyaları parçalar halinde buraya yazıyorum:

"boyaci.d" iki farklı controller'dan oluşuyor:

module boyaci;
 
import std.stdio;
import std.random;
import kagit;
import yer;
 
class RasgeleBoyayan {
    size_t satırAdedi;
    size_t sütunAdedi;
    size_t rasgeleNoktaAdedi;
 
    this(size_t satırAdedi, size_t sütunAdedi, size_t rasgeleNoktaAdedi) {
        this.satırAdedi = satırAdedi;
        this.sütunAdedi = sütunAdedi;
        this.rasgeleNoktaAdedi = rasgeleNoktaAdedi;
    }
 
    void kullan(Kağıt kağıt) {
        dchar[Yer] noktalar;
 
        foreach (i; 0 .. rasgeleNoktaAdedi) {
            immutable yer = Yer(uniform(0, satırAdedi), uniform(0, sütunAdedi));
            immutable renk= 'a' + uniform(0, 26);
            noktalar[yer] = renk;
        }
 
        kağıt.boya(noktalar);
    }
}
 
// Noktayı kağıt üzerinde yürütür; noktanın daha önceden geçtiği yerleri de
// belirtir.
class NoktaYürüten {
    enum uçRengi = 'X';
    enum gövdeRengi = 'o';
 
    Yer uç;
 
    this(Yer başlangıç) {
        this.uç = başlangıç;
    }
 
    void kullan(Kağıt kağıt) {
        kağıt.boya([ uç : uçRengi ]);
 
        writeln("Lütfen noktanın yolunu belirtiniz");
 
        bool devam_mı = true;
 
        while (devam_mı) {
            write("(w: Yukarı, z: Aşağı, a: Sola, s: Sağa, 0: Çıkış) ? ");
            dchar yön;
            readf(" %s", &yön);
 
            immutable eskiUç = uç;
 
            switch (yön) {
            case 'w': uç = uç.yukarıdaki; break;
            case 'z': uç = uç.aşağıdaki;  break;
            case 'a': uç = uç.soldaki;    break;
            case 's': uç = uç.sağdaki;    break;
            case '0': devam_mı = false;   break;
            default: writeln("Hatalı giriş!"); break;
            }
 
            kağıt.boya([ eskiUç : gövdeRengi, uç : uçRengi ]);
        }
    }
}

"gosterici.d" iki farklı view'dan oluşuyor:

module gosterici;
 
import std.stdio;
import kagit_ilgilisi;
 
// Kağıdın durumunu satır bilgileri halinde gösterir
class ListeleyenGösterici : Kağıtİlgilisi {
    dchar zeminRengi;
 
    this(dchar zeminRengi) {
        this.zeminRengi = zeminRengi;
    }
 
    void değişti(const dchar[][] noktalar) {
        writeln("\n=== Zemin Renginde Olmayan Noktalar ===");
 
        foreach (satır, satırNoktaları; noktalar) {
            foreach (sütun, renk; satırNoktaları) {
                if (renk != zeminRengi) {
                    writefln("%s,%s: %s", satır, sütun, renk);
                }
            }
        }
    }
}
 
// Kağıdı iki boyutlu olarak gösterir
class İkiBoyutluGösterici : Kağıtİlgilisi {
    void değişti(const dchar[][] noktalar) {
        foreach (i, satır; noktalar) {
            writefln("%3s| %s", i, satır);
        }
    }
}

"kagit.d" Kağıt türünü tanımlıyor:

module kagit;
 
import kagit_ilgilisi;
import yer;
 
class Kağıt {
    dchar[][] kareler;          // Kağıdı oluşturan kareler (tuval)
    Kağıtİlgilisi[] ilgililer;  // Bu kağıttaki değişiklerden haberdar olmak
                                // isteyen ilgililer (observers)
 
    this(size_t satırAdedi, size_t sütunAdedi, dchar zemin) {
        auto boşSatır = new dchar[sütunAdedi];
        boşSatır[] = zemin;
 
        foreach (i; 0 .. satırAdedi) {
            this.kareler ~= boşSatır.dup;
        }
    }
 
    // Bu, "observer pattern"da adı geçen registerObserver() işlevidir
    void ilgiliyiTanı(Kağıtİlgilisi ilgili) {
        this.ilgililer ~= ilgili;
    }
 
    // Kağıdı verilen beneklerle doldurur
    void boya(dchar[Yer] benekler) {
        foreach (yer, renk; benekler) {
            kareler[yer.satır][yer.sütun] = renk;
        }
 
        // Yeni benekler oluştuğunu ilgililere haber ver
        foreach (ilgili; ilgililer) {
            // Bu, "observer pattern"daki notify()'dır
            ilgili.değişti(kareler);
        }
    }
}

"kagit_ilgilisi.d" kağıttaki değişiklerden haberdar olmak isteyenlerin gerçekleştirmeleri gereken arayüzü bildiriyor:

module kagit_ilgilisi;
 
// Bu, "observer pattern"daki observer'dır.
//
// Kağıt'taki değişikliklerden haberdar olmak isteyenlerin gerçekleştirmeleri
// gereken arayüz. Kağıt, ilgilileri haberdan etmek için değişti() işlevini
// çağıracak.
interface Kağıtİlgilisi {
    // Bu, "observer pattern"daki notify() işlevidir.
    void değişti(const dchar[][] noktalar);
}

"yer.d" satır ve sütun bilgisinden oluşan ve bir kaç yararlı işlev sunan bir tür içeriyor:

module yer;
 
struct Yer
{
    size_t satır;
    size_t sütun;
 
    Yer yukarıdaki() const @property {
        return Yer(satır - 1, sütun);
    }
 
    Yer aşağıdaki() const @property {
        return Yer(satır + 1, sütun);
    }
 
    Yer sağdaki() const @property {
        return Yer(satır, sütun + 1);
    }
 
    Yer soldaki() const @property {
        return Yer(satır, sütun - 1);
    }
}

"main.d" MVC tasarımının getirdiği esnekliği göstermek için birisi -version seçeneği ile seçilebilen iki farklı main() içeriyor:

module main;
 
import std.stdio;
import kagit;
import gosterici;
import boyaci;
import yer;
 
enum satırAdedi = 5;
enum sütunAdedi = 10;
enum zeminRengi = '.';
 
version (etkiles) {
 
void main()
{
    // Model: Bir kağıt nesnesi oluşturuyoruz
    auto kağıt = new Kağıt(satırAdedi, sütunAdedi, zeminRengi);
 
    // View: Bir view nesnesini kağıda tanıtıyoruz
    auto gösterici = new İkiBoyutluGösterici();
    kağıt.ilgiliyiTanı(gösterici);
 
    // Controller: Kağıtta değişiklikler yapacak olan nesneyi oluşturuyoruz
    immutable ortaNokta = Yer(satırAdedi / 2, sütunAdedi / 2);
    auto boyacı = new NoktaYürüten(ortaNokta);
 
    boyacı.kullan(kağıt);
}
 
} else { // version
 
void main()
{
    // Model: Bir kağıt nesnesi oluşturuyoruz
    auto kağıt = new Kağıt(satırAdedi, sütunAdedi, zeminRengi);
 
    // View: İki farklı view nesnesini kağıda tanıtıyoruz
    kağıt.ilgiliyiTanı(new İkiBoyutluGösterici());
    kağıt.ilgiliyiTanı(new ListeleyenGösterici(zeminRengi));
 
    // Controller: Kağıtta değişiklikler yapacak olan nesneyi oluşturuyoruz
    enum rasgeleBenekAdedi = 10;
    auto boyacı = new RasgeleBoyayan(satırAdedi, sütunAdedi, rasgeleBenekAdedi);
 
    boyacı.kullan(kağıt);
}
 
} // version 

Derlemek için:

dmd boyaci.d gosterici.d kagit.d kagit_ilgilisi.d main.d yer.d  -ofmvc_deneme  -unittest -property -w -gc -debug

O satır yukarıda verdiğim çıktıyı üretir. Aşağıdaki satır ise kullanıcının programla etkileştiği ve ekranda yılan gibi ilerletebildiği bir nokta oluşturur (Ekran dışına çıkıldığında hata atıyor; kısa olsun diye onlarla ilgilenmedim):

dmd ... -version=etkiles

Ali
Doğrulama Kodu: VeriCode Lütfen resimde gördüğünüz doğrulama kodunu girin:
İfadeler: :-) ;-) :-D :-p :blush: :cool: :rolleyes: :huh: :-/ <_< :-( :'( :#: :scared: 8-( :nuts: :-O
Özel Karakterler:
Sayfa:  1  2  sonraki 
Forum: Ders Arası RSS
Bağlı değilsiniz. · Şifremi unuttum · ÜYELİK
This board is powered by the Unclassified NewsBoard software, 20100516-dev, © 2003-10 by Yves Goergen
Şu an: 2017-11-21, 15:16:02 (UTC -08:00)