Nesneye yönelik kavramları ve programlamayı öğretmek için bazı gerçek pratik örnekler (python'da)

Dec 31 2020

Lise öğrencilerime "Python ile Sıkıcı Şeyleri Otomatikleştir" den bazı fikirler alarak bazı basit python betikleri yazmayı öğrettim , örneğin bir klasördeki dosya adlarını belirli bir adlandırma modeliyle değiştirmek.

Bir sonraki hedefim, onlara nesne yönelimli kavramları öğretmek ve mümkünse kendilerine denemeleri için bazı "gerçek" örnekler vermek. Ancak, OO programlama (yalnızca python değil) hakkında bulduğum malzemelerin çoğu (google'ın en iyi arama sonuçlarının çoğu), eğer söyleyebilirsem, zayıf örneklerle dolu. Onlara OO konseptini ve OOP'yi öğretmek için araba/araç veya hayvan/köpek kullanmayı gerçekten sevmiyorum.

A. sıkıcı (lise çocuklarını bilirsiniz)
B. gerçek bir faydası yok.
C. bir yorumun dediği gibi "ölümcül kusurlular." (Ancak hayvan örneğini kullanmayı gerçekten seviyorsanız, bu "Nesneye Yönelik Tasarım" ı kontrol edin )

Pathlib kullanmayı planlıyorum (btw, Windows ve UNIX arasındaki fark hakkında zaten temel bir fikirleri var)

GUI geliştirme, OOP öğretmek için başka bir iyi örnek olabilir, ancak onlara henüz GUI öğretmek istemiyorum.

OO'yu "gerçek" örnekle tanıtmak için herhangi bir öneriniz var mı?

BTW, nesne yönelimli programlamanın güçlü bir savunucusu değilim. Ancak pathlib.Pathsınıf, özellikle os.path ile karşılaştırıldığında yararlı bir soyutlamadır, ancak lise öğrencilerinin bunu takdir etmesi pek olası değildir.

---- Güncelleme ----

Python kayıt modülünün başka bir iyi örnek olduğunu düşünüyorum, ancak deneyimli olmayan programcılar için çok karmaşık görünüyor.

Kayıt modülünü kullanırken esas olarak Logger, Handler, Formatter olmak üzere 3 nesne kullanırız . Logger, cephe olarakhttps://docs.python.org/3/howto/logging.html#loggers dedim,

Kaydedici nesnelerinin üçlü bir işi vardır. İlk olarak, uygulamaların mesajları çalışma zamanında günlüğe kaydedebilmesi için uygulama koduna çeşitli yöntemler sunarlar. İkinci olarak, günlükçü nesneleri, önem derecesine (varsayılan filtreleme olanağı) veya filtre nesnelerine göre hangi günlük mesajlarının etkileneceğini belirler. Üçüncüsü, günlükçü nesneleri ilgili günlük mesajlarını tüm ilgili günlük işleyicilerine iletir.

İşleyici, kalıtımı kullanmak için iyi bir örnektir https://docs.python.org/3/howto/logging.html#useful-handlers ve işleyiciler, LogRecord'un karmaşıklığını biçim günlüğü mesajına gizleyen biçimlendiricileri kullanır.

Bu sınıflara ek olarak, rahat kullanım için bir dizi modül düzeyinde işlev vardır .

Genel olarak, kompozisyon ve toplama kullanımına iyi bir örnektir .

Ancak python logging modülünde uzman değilim, umarım python logging modülüne aşina olan biri soruma bir cevap ekleyebilir.

--- güncelleme 2 ----

Alan Kay'in "İyi yazılım mühendisliği uygulaması için önemli olduğunu düşündüğünüz nesne yönelimli paradigmanın beş özelliği nelerdir? Bu sözlerle bence Buffy'nin verdiği cevap yankılanıyor. Ancak bu fikirlerin lise öğrencilerine veya tecrübesiz programcılara nasıl aktarılacağı da bir başka zorluktur.

içini dışarıdan koruyabilen bir “parça” yapısı ve bunun tersi

etkileşimleri iletebilen ve bağımlılıklarla başa çıkabilen bir “iletişim” yapısı

Bir parçaya yinelemeli olarak sığabilen ve her şey bu şekilde yapılan iletişim ve parçaların birleşiminden oluşan bir “sistem” yapısı

iletilen “mesajlar” da sistemler açısından

yapılmakta olan sistem aynı tür sistemlerle yapılıyor...

Yanıtlar

3 Buffy Dec 31 2020 at 21:53

Bunu birkaç kez güncellemeyi umuyorum ve sonunda uzun bir cevap almayı umuyorum.

Ancak, zaten farkında olabileceğiniz bazı şeyleri açıklayarak başlayayım, ancak diğer okuyucular olmayabilir.

Birincisi, OO programlama temelde kalıtımla ilgili değildir ve çok fazla kitap ve yazar bunu anlayamaz. Ayrıca, kalıtımı, yazılımın anlaşılmasını ve bakımını zorlaştıran korkunç bir şekilde kullanırlar.

Örneğin, biyolojik dünyanın Linnaeus tarzı hiyerarşisinin neredeyse tamamen "arayüzlerden" biri olduğunu ve hatta "soyut sınıflar" bile değil, çok daha az somut sınıflar olduğuna dikkat edin. Örneğin, somutlaştırılmış "memeliler" yoktur. Şeyler, fikirler dışında, yalnızca hiyerarşinin izniyle var olurlar. Elbette genetik bir devamlılık var.

İkincisi, OO tasarımını yazarken ve öğretirken size rehberlik edebilecek bazı ilkeler vardır, ancak bunlar disiplin gerektirir. Ve bu disiplinin bir parçası da, kuralları "ihlal ettiğiniz" durumları kontrol etmektir.

OO programcısı olmak isteyen herkese ilk tavsiyem, kalıtım açısından değil, kompozisyon açısından düşünmektir. Karmaşık şeyler (nesneler) , içerdiği nesneden biraz daha basit olan ve ona bazı temel hizmetler sağlayan diğer şeylerden (nesneler) oluşur . Örnek değişkenlerinizin tümünün (hatta çoğunun) dil ilkelleri olduğu sınıflar yazarsanız, bunu gerçekten anlamazsınız. Ve eğer bu örnek değişkenler (nesneler veya ilkel) çok fazla alıcı ve ayarlayıcıya sahipse, o zaman hiç OO Programlama yapmıyorsunuz demektir.

Aslında, yukarıdaki hataları yapmak, programcının, OO'nun kararları almak için tasarlandığı programın tüm noktalarında tüm ayrıntıları takip etmesini gerektirir, böylece tekrar "kontrol edilmeleri" gerekmez. Ayarlayın ve tabiri caizse unutun.

Bu nedenle, bir otomobil yapmak istiyorsanız, bunu bir araç alt sınıfı olarak düşünmeyin (bu size hiçbir şey kazandırmaz: kişisel drone ve savaş gemisi her ikisi de araçtır). Çeşitli parçalardan oluşmak yerine düşünün: motor, şanzıman, kontroller, konaklama yerleri, vb. Ve bu parçaların kendileri de parçalardan oluşuyor. Bir motor bulunur , vb ateşleyiciler ve pistonları, egzoz da bölümden oluşan bir çoğu. İlkellerle inşa ettiğiniz yalnızca en düşük, en basit düzeydedir.

Akılda tutulması ve hemen hemen her zaman takip edilmesi gereken ilkelerden ikisi, Liskov İkame İlkesi ve Demeter Yasasıdır .

İlki, bir sınıfı bir alt sınıfla genişletirseniz, alt sınıfın genel arabirimini de genişletmeyeceğinizi önerir. O zaman, tüm alt sınıf nesneleri ikame edilebilir, davranışları farklıdır, ancak arayüzde değildir. Demeter ise, ilişkileri bir okuyucu için netleştiren daha açık kod yazmaya zorlar. Tabii ki sizi daha fazla isim vermeye zorlar ve eğer niyetleri isimleri ifşa etmekse , kodunuz daha açıktır.

Liskov ilkesi, elbette, sizin de düşüncenize dahil etmeniz gereken SOLID'in unsurlarından biridir .

Kendi programlamamda Liskov'a çok bağlıyım, ayrıca sınıfları yazmadan önce çoğu şey için Arayüzler tanımlıyorum. Savaşın hararetinde, ardı ardına gelen mesajlarda Demeter'e daha az bağlıyım. Ama bazen gerçekten ne demek istediğimi anlamak için bu abcd türlerini çözmem gerekiyor.

OO kodu yazarken sahip olduğum bir hedef, minimum yapı ile yalnızca çok kısa yöntemler yazmaya çalışmaktır. Başka bir deyişle, Döngüsel Karmaşıklığı en aza indirmeye çalışıyorum . Bir yöntemde dördüncü ifadeden sonra veya karmaşıklık seviyesi üçe ulaştığında avuç içlerim kaşınmaya başlar. Bundan her zaman kurtulamam, ama bu bir hedef. Çözüm, karmaşıklığı acımasızca yeniden düzenlemektir. Yöntemleri, elbette, ama aynı zamanda karmaşıklığı yönetmek için yeni sınıflar yaratan "parçaları" hesaba katın. Bu sınıfların çoğu Singleton olsa bile, kod genellikle geliştirilir ve başlangıçtan itibaren amacın akılda tutulması, yeniden düzenleme adımına daha az ihtiyaç duyulduğu anlamına gelir.

Tasarım kalıpları, çoğu OO dilinde etkili bir programcı olmanız için ihtiyaç duyduğunuz araçlardır. Özellikle yararlı olanlar Strateji, Dekoratör, Gözlemci ve Yineleyicidir. Bunların çoğu aslında çeşitli Java kitaplıklarını oluşturmak için kullanılır.


Şimdi burada sorulan asıl soruya gelelim. Ancak, öğrenciler ayrılan sürede projeyi asla tamamlamasalar bile birçok öğrenmenin gerçekleşebileceğini unutmayın. Geliştirmeye yönelik çevik bir yaklaşım (Aşırı Programlama, örneğin, "Müşteri" olarak sizinle) tüm özellikler uygulanmasa bile onlara bazı işlevler bırakır .

Zindan Oyunu

Metin tabanlı bir zindan oyunu oluşturun. Ana nesneler karakterler (insanlar), yerler ve şeylerdir. Yerler bir tür harita, labirent veya ızgara şeklinde düzenlenmiştir. Karakterler yerlere girdiğinde şeyler olur. Karakterler bir şeyler bulur ve taşır. Şeylerin türüne bağlı olarak eylemleri vardır. Bir "büyü", eylemi bağlama bağlı olabilecek bir "şey"dir. Bir "taşıyıcı" nesne farklı odalarda (strateji nesneleri) farklı şekilde çalışabilir.

Klasik tahta oyunu Chutes (veya Snakes) ve Ladders'ın bunun basitleştirilmiş bir versiyonu olduğunu unutmayın. Metin tabanlı bir sürüm, çok fazla karmaşıklığı önler.

Hesap makinesi

Hesap makinesinde tuşlar ve ekran gibi parçalar bulunur. Daha az görünür olan dahili bellek, muhtemelen bir yığındır. İşlemler bile nesne olabilir. Anahtarların davranışı, hesaplamanın durumuna (Strateji modeli) bağlı olarak değişir. Aslında tek bir IF ifadesi olmadan basit bir hesap makinesi oluşturmak mümkündür.

Assembly diline sahip soyut bilgisayar

Yığın Tabanlı bir bilgisayar işlemci simülasyonu oldukça basittir. Akümülatörler ve benzeri olabilir, ancak tüm işlemlerin yapıldığı tek bir yığın basit ve eksiksizdir. İşlemler nesneler (parçalar) olabilir. Bu örneğin bir avantajı, gerekli yöntemlerin çoğunun çok kısa olabilmesidir. Bir program, bir Java Tarayıcı nesnesi ile okunabilir. Dil alt programları destekleyecekse, en azından bir program sayacına ve muhtemelen bir çerçeve işaretçisine ihtiyacı vardır.

Yapışkan Notlar (bunu denemedim)

Kullanıcıların not tutmasına ve çapraz referans vermesine ve bunları düzenlemesine izin veren bir uygulama. Bazı sınıflar Notes, Anahtar Sözcükler, Bağlantılar, Listeler olabilir.

tehlike

TV'den tehlikeli oyun için bir simülatör. Kategoriler, Cevaplar, Sorular, Takımlar, Puanlar.

(Yakında geri döner, belki).

1 meuh Feb 05 2021 at 22:10

Yazması çok basit olan bir Python sınıfı buldum ("sıradan" görevler için), geliştirme sırasında kodu basitleştirme ve yeniden düzenleme arzusundan doğal olarak büyüyebilirler. Daha çok pragmatik bir aşağıdan yukarıya yaklaşımdır; belirli bir amaç için bazı basit kodları hacklemeyi bekliyordunuz, sonra biraz büyüyor ve demetler, diziler ve hatta küreseller olan "yapıları" işleyen çeşitli işlevleriniz olduğunu görüyorsunuz. Aniden ışığı görüyor ve bir sınıf oluşturuyorsunuz ve kod boyutu 2 veya daha fazlasına bölünüyor ve çok daha basit .

Bu, bazı "düz" somut mevcut kodlar alarak ve yapılabilecek soyutlamayı arayarak, verileri işlev parametrelerinden sınıfa taşıyarak ve self.

Kalıtım gibi şeyler benzer şekilde neredeyse doğal olarak keşfedilebilir; istediğinizi tam olarak yapmayan mevcut bir sınıfı kullanıyorsunuz ve onu değiştirmeniz gerekiyor; kopyalamak ve değiştirmek yerine, bir yöntemi alt sınıflar ve değiştirir veya eklersiniz.

Somut örnekler için bilgisayar donanımının kendisine bakabilirsiniz. Düşük düzeyde, yazmaçlar genellikle birkaç farklı işleve bölünür. Bir özellik bitini 1'e ayarlamak istiyorsanız, onu 20 bit sola kaydırmanız, kaydın geçerli değerini okumanız, 0'dan 5'e kadar olan bitleri "temizlemek için 1 yaz" oldukları için maskelemeniz vb. gerekir. 16550 uart seri portunu taklit etmeyi deneyin; ruha iyi gelir. Ve elbette, gerçek bir mikroişlemci üzerinde çalışan MicroPython kullanıyorsanız, muhtemelen kodunuzu bile deneyebilirsiniz.

Flater Jun 02 2021 at 16:36

Hem veri modelleme hem de OOP (her iki durumda da bir tür normalleştirme etrafında dönen) için ilk örneğim bir video kiralama mağazası . Bu belki çok eski bir örnek olabilir, başka şeyler için onu bir kitaplık veya kiralık bir mağaza olarak değiştirmekten çekinmeyin, ancak video mağazası örneğinin hem OOP'nin inceliklerini hem de veri normalleştirmesini vurguladığını ve aynı zamanda çok basit olduğunu görüyorum. kavramak için bağlam.

Ana amaç, üç tablo/sınıf diyagramı oluşturmaktır: Customer, Videove Rental(müşteriler ve videolar arasındaki çapraz tablodur).

Bu cevabın geri kalanı, belirli OOP temellerinin eldeki örnekle nasıl ilişkilendirileceğine dair ipuçlarıdır.


Neden nesneler?

Peki, kiralama için üç şeyi izlemek istiyorsanız video verilerinizi nasıl depolarsınız: müşteri adı, adresi, video adı ve beklenen iade tarihi.

Öğrencilerden, Alice'in Antz'ı, Bob'un Bee Movie'yi ve Charlie'nin Arabaları kiraladığını söyleyebilecek çok basit bir program yazmalarını isteyin. Yeniden kullanılabilir bazı PrintRentalInfoyöntemlere güvenmelerine izin verin, ancak yöntem parametrelerini uygun gördükleri şekilde tanımlamalarına izin verin.

Henüz OOP görmemiş olan öğrenciler dört farklı dizi kullanacak ve dört dizinin tümünde aynı dizinde bir videonun bulunduğu gerçeğine güveneceklerdir. Onlara müşteri adlarından oluşan bir "torba", bir adres "torbası", bir video adı "torbası" ve bir iade tarihi "torbası" ile çalışmanın gerçekten kolay olmadığını açıklayın. Veri alanı başına bir "çanta" yapmak yerine, kiralama başına bir "çanta" yapmamızın daha mantıklı olacağı fikrini önerin.

RentalDört özellik ile sınıfı oluşturun . Onlarla aynı uygulamayı oluşturun, ancak OOP kullanarak. Bu onlara nesne başlatmayı, farklı nesnelerin nasıl aynı yapıya ancak bireysel olarak benzersiz içeriğe sahip olduğunu ve bir nesneyi nasıl etrafa aktarabileceğinizi (ilkel türlerin çoklu yöntem parametrelerinin aksine) gösterecektir.

İlgili tüm bilgileri bir arada tutarak bu kiralık "çantayı" bir yöntemden diğerine taşımanın ne kadar kolay olduğunu gerçekten vurgulayın.


Neden birden fazla sınıf?

Dördüncü bir müşteri belirir. Onun adı da Alice. Burada bir sorunla karşılaşıyorsunuz çünkü artık hangi Alice'in hangi videoları kiraladığını söyleyemiyorsunuz ve yanlış Alice'e ceza vermek istemiyorsunuz.

Ayrıca, orijinal Alice, adresinin değiştiğini bize bildirmek için bizi aradı. Tüm kiralamaları gözden geçirmenin ve "eski_adresi" körü körüne "yeni_adres" olarak değiştiremeyeceğinizi anlamanın zorluğuna dikkat edin, çünkü aynı adreste yaşayan ve Alice ile taşınmamış başka müşteriler olabilir. Ayrıca, dördüncü Alice de zaten bazı kiralamalar yaptığı için isme de güvenemezsiniz.

Öğrenciler isim ve adres kombinasyonuna göre bunu yapabileceğinizi protesto etmeye devam ederse, bu iki Alice aynı adreste yaşarsa ve sadece biri hareket ederse ne olur.

Öğrencilere, tüm müşterilerimizi ve adreslerini ayrı bir liste halinde almamızın çok mantıklı olacağını önerin, böylece onları adlarından daha fazlasıyla ayırt edebilelim ve bir kişinin ayrıntılarını kolayca değiştirebilelim.

Hedef: bir Customersınıf oluşturun ve ad/adres özelliklerinin aksine Rentalbir Customerözellik içerecek şekilde değiştirin.

Odaklanma: Adları ve adresleri aynı olsa bile iki farklı müşteri nesnesine nasıl sahip olabileceğinizi çok vurgulayın .

Ekstra: VideoSınıfı oluştururken aynı yaklaşımı benimseyebilirsiniz , böylece sahip olduğunuz belirli videoları izleyebilirsiniz. Belki bunu öğrencilere bir alıştırma olarak bırakın, çünkü bu öncekiyle hemen hemen aynıdır.


Bu noktadan itibaren, sergilemek istediğiniz şeye göre iş mantığını genişletebilirsiniz.

  • Kalıtım - Belki mağaza kiralar Videove Game, ancak yine Rentalde bunlardan herhangi birine bağlanabilmek istiyorsunuz (temel RentableObjectsınıfı kullanarak )
  • Arayüz - Aynı örneği miras olarak kullanabilirsiniz.
  • Veri normalleştirme - Hem elimizdeki filmleri hem de bireysel fiziksel kasetleri nasıl takip edeceğiz (aynı filmden birden fazla olabilir)? Bir müşterinin maruz kaldığı ve hangilerini daha önce ödediği para cezalarını nasıl takip edebilecek?
  • Veri dönüşümü - Bizim patron yapılan tüm kiralamalarda haftalık rapor yazdırmak istiyor, kiralama, dan önce hangi tahakkuk cezaları ve para cezaları iade hala ödenmiş değil.
  • Rakip değeri Referans - Bir ekleyin Pricehem Videove Rental. Vitrin sen seti nasıl rental.Pricedayanır video.Price, ama ne zaman video.Pricedeğişiklikleri daha sonra, rental.Pricebakir. Şimdi aynı alıştırmayı bir referans nesne ile tekrarlayın (örn. müşterinin adını değiştirmek).

Bu örneğin bağlamını kavramanın çok kolay olduğunu ve genişleme için birçok fırsatı olduğunu düşünüyorum. Bu, genişletmeye devam edeceğiniz uzun vadeli bir projeye dönüşebilir; bu, öğrencilerinize değişen gereksinimlerin nasıl ele alınacağını ve temiz kodlamanın veya bakım ve eski geliştirmenin yararlarını öğretmek istiyorsanız değerli bir ders olabilir.