Sınıfım neden kitaptaki sınıflar hiyerarşisinden daha kötü (acemi OOP)?


11

PHP Nesneleri, Desenler ve Pratik okuyorum . Yazar bir üniversitede bir ders modellemeye çalışıyor. Amaç ders tipini (ders veya seminer) ve ders ücretini saatlik veya sabit fiyat dersine bağlı olarak vermektir. Yani çıktı

Lesson charge 20. Charge type: hourly rate. Lesson type: seminar.
Lesson charge 30. Charge type: fixed rate. Lesson type: lecture.

giriş aşağıdaki gibi olduğunda:

$lessons[] = new Lesson('hourly rate', 4, 'seminar');
$lessons[] = new Lesson('fixed rate', null, 'lecture');

Bunu ben yazdım:

class Lesson {
    private $chargeType;
    private $duration;
    private $lessonType;

    public function __construct($chargeType, $duration, $lessonType) {
        $this->chargeType = $chargeType;
        $this->duration = $duration;
        $this->lessonType = $lessonType;
    }

    public function getChargeType() {
        return $this->getChargeType;
    }

    public function getLessonType() {
        return $this->getLessonType;
    }

    public function cost() {
        if($this->chargeType == 'fixed rate') {
            return "30";
        } else {
            return $this->duration * 5;
        }
    }
}

$lessons[] = new Lesson('hourly rate', 4, 'seminar');
$lessons[] = new Lesson('fixed rate', null, 'lecture');

foreach($lessons as $lesson) {
    print "Lesson charge {$lesson->cost()}.";
    print " Charge type: {$lesson->getChargeType()}.";
    print " Lesson type: {$lesson->getLessonType()}.";
    print "<br />";
}

Ama kitaba göre yanılıyorum (benim de eminim). Bunun yerine, yazar çözüm olarak büyük bir sınıf hiyerarşisi verdi. Önceki bir bölümde yazar, sınıf yapımı değiştirmeyi düşünmem gereken zaman olarak aşağıdaki 'dört tabela' ifadesini kullandı:

Görebildiğim tek sorun koşullu ifadeler ve bu da belirsiz bir şekilde - öyleyse neden bunu yeniden düzenleyelim? Gelecekte tahmin edemediğim hangi problemlerin ortaya çıkabileceğini düşünüyorsunuz?

Güncelleme : Bahsetmeyi unuttum - bu yazarın bir çözüm olarak sağladığı sınıf yapısı - strateji modeli :

Strateji modeli


29
Bir PHP kitabından nesne tabanlı programlamayı öğrenmeyin.
giorgiosironi

4
@giorgiosironi Dilleri değerlendirmekten vazgeçtim. PHP'yi günlük olarak kullanıyorum ve bu yüzden yeni kavramları öğrenmek benim için en hızlısı. Her zaman bir dilden nefret eden insanlar vardır (Java: slidesha.re/91C4pX ) - kuşkusuz, Java haters'ı bulmak PHP'nin çözdüğünden daha zordur, ancak yine de. PHP'nin OO'su ile ilgili sorunlar olabilir, ancak şimdilik Java sözdizimini, "Java yolunu" ve OOP'yi öğrenmek için zaman ayıramıyorum. Ayrıca, masaüstü programlama bana yabancı. Ben tam bir web insanıyım. Ancak JSP'ye ulaşmadan önce, masaüstü Java'sını bilmeniz gerektiği iddia ediliyor (Alıntı yok, yanlış mıyım?)
Aditya MP

Dizeler yerine "sabit ücret" / "saatlik ücret" ve "seminer" / "ders" için sabitleri kullanın.
Tom Marthenal

Yanıtlar:


19

Kitabın bunu açıkça belirtmemesi çok komik, ama aynı sınıftaki ifadelerin muhtemelen Açık / Kapalı prensibi olması durumunda sınıf hiyerarşisini desteklemesinin nedeni Yaygın olarak bilinen bu yazılım tasarım kuralı, bir sınıfın modifikasyon ancak uzatma için açık.

Örneğinizde, yeni bir ders türü eklemek Ders sınıfının kaynak kodunu değiştirmek anlamına gelir, bu da onu kırılgan ve regresyon eğilimli hale getirir. Her ders türü için bir temel sınıf ve bir türeviniz varsa, bunun yerine genellikle daha temiz olarak kabul edilen başka bir alt sınıf eklemeniz gerekir.

İsterseniz, bu kuralı "Koşullu İfadeler" bölümüne koyabilirsiniz, ancak bu tabelayı biraz belirsiz buluyorum. İfadeler genellikle sadece kod kokuyorsa, belirtiler. Çok çeşitli kötü tasarım kararlarından kaynaklanabilirler.


3
Kalıtım yerine işlevsel bir uzantı kullanmak çok daha iyi bir fikir olacaktır.
DeadMG

6
Soruna kesinlikle başka / daha iyi yaklaşımlar var. Ancak, bu açıkça tıpkı bana vurdu nesne yönelimli programlama ve açık / kapalı prensibi hakkında bir kitap OO tasarım ikilem bu tür yaklaşan klasik yoluna.
guillaume31

Soruna en iyi şekilde yaklaşmaya ne dersiniz ? Sadece OO olduğu için daha düşük bir çözüm öğretmenin bir anlamı yok.
DeadMG

16

Kitabın öğretmeyi amaçladığı şeyin şunun gibi şeylerden kaçınmak olduğunu düşünüyorum:

public function cost() {
    if($this->chargeType == 'fixed rate') {
        return "30";
    } else {
        return $this->duration * 5;
    }
}

Kitabın yorumu, üç dersinizin olması gerektiğidir:

  • AbstractLesson
  • HourlyRateLesson
  • FixedRateLesson

Daha sonra costişlevi her iki alt sınıfta da yeniden uygulamalısınız .

Benim kişisel fikrim

bunun gibi basit durumlar için: yapma. Basit koşullu ifadelerden kaçınmak için kitabınızın daha derin sınıf hiyerarşisini desteklemesi gariptir. Dahası, girdi spesifikasyonunuzla birlikte, Lessonkoşullu bir ifade olan bir sevkıyata sahip bir fabrika olması gerekecektir. Yani kitap çözümü ile sahip olacaksınız:

  • 1 yerine 3 sınıf
  • 0 yerine 1 kalıtım düzeyi
  • aynı sayıda koşullu ifade

Bu ek bir fayda olmadan daha karmaşık.


8
In Bu durumda , evet, çok fazla karmaşıklık var. Ama daha büyük bir sistem içinde, daha fazla ders gibi ekleyecek SemesterLesson, MasterOneWeekLessonvb soyut kök sınıfı veya daha iyisi bir arayüz, daha sonra gitmek için yolu kesinlikle. Ancak sadece iki vakanız olduğunda, yazarın takdirine bağlıdır.
Michael K

5
Genel olarak sana katılıyorum. Bununla birlikte, eğitim örnekleri bir noktayı açıklamak için sıklıkla tasavvur edilir ve aşırı mimaridir. Elinizdeki konuyu karıştırmamak ve bu hususları daha sonra tanıtmak için bir an için diğer hususları görmezden geliyorsunuz.
Karl Bielefeldt

+1 İyi bir arıza. "Bu ek bir fayda olmadan daha karmaşık" yorum mükemmel programlama 'fırsat maliyeti' kavramını göstermektedir. Teori! = Pratiklik.
Evan Plaice

7

Kitaptaki örnek oldukça garip. Sorunuzdan, orijinal ifade:

Amaç ders türü (Ders veya Seminer) ve ders ücretlerini saatlik veya sabit fiyat dersine bağlı olarak vermektir.

Bu, OOP ile ilgili bir bölüm bağlamında, yazarın muhtemelen aşağıdaki yapıya sahip olmanızı istediği anlamına gelir:

+ abstract Lesson
    - Lecture inherits Lesson
    - Seminar inherits Lesson

+ abstract Charge
    - HourlyCharge inherits Charge
    - FixedCharge inherits Charge

Ama sonra alıntıladığınız girdi geliyor:

$lessons[] = new Lesson('hourly rate', 4, 'seminar');
$lessons[] = new Lesson('fixed rate', null, 'lecture');

yani dize olarak yazılan kod adı verilen ve dürüst olmak gerekirse, bu bağlamda okuyucunun OOP öğrenmesi amaçlandığında korkunç bir şeydir .

Bu ayrıca, kitap yazarının niyetinde haklı olduğum anlamına gelirse (yani, yukarıda listelediğim soyut ve kalıtsal sınıfları oluşturmak), bu yinelenen ve oldukça okunamayan ve çirkin koda yol açacaktır:

const string $HourlyRate = 'hourly rate';
const string $FixedRate = 'fixed rate';

// [...]

Charge $charge;
switch ($chargeType)
{
    case $HourlyRate:
        $charge = new HourlyCharge();
        break;

    case $FixedRate:
        $charge = new FixedCharge();
        break;

    default:
        throw new ParserException('The value of chargeType is unexpected.');
}

// Imagine the similar code for lecture/seminar, and the similar code for both on output.

"Tabelalar" gelince:

  • Kod Çoğaltma

    Kodunuzda çoğaltma yok. Yazarın yazmanızı beklediği kod bunu yapar.

  • Bağlamı Hakkında Çok Şey Bilen Sınıf

    Sınıfınız yalnızca dizeleri girdiden çıktıya geçirir ve hiçbir şey bilmez. Öte yandan beklenen kod, çok fazla şey bildiği için eleştirilebilir.

  • Tüm Ticaretlerin Jack'i - Birçok şey yapmaya çalışan sınıflar

    Yine, sadece ipleri geçiyorsunuz, başka bir şey değil.

  • Koşullu İfadeler

    Kodunuzda bir koşullu ifade var. Okunabilir ve anlaşılması kolaydır.


Belki de, yazar, yukarıda altı sınıftan daha çok yeniden düzenlenmiş bir kod yazmanızı bekledi. Örneğin, fabrika desenini dersler ve ücretler vb. İçin kullanabilirsiniz .

+ abstract Lesson
    - Lecture inherits Lesson
    - Seminar inherits Lesson

+ abstract Charge
    - HourlyCharge inherits Charge
    - FixedCharge inherits Charge

+ LessonFactory

+ ChargeFactory

+ Parser // Uses factories to transform the input into `Lesson`.

Bu durumda, aşırı mimarinin mükemmel bir örneği olacak dokuz sınıfla karşılaşacaksınız .

Bunun yerine, on kat daha kısa, anlaşılması kolay, temiz bir kod buldunuz.


Vay canına, tahminlerin kesin! Yüklediğim resme bakın - yazar aynı şeyleri tavsiye etti.
Aditya MP

Ben ediyorum bu yüzden bu cevabı kabul etmek seviyorum, ama aynı zamanda @ ian31 cevabı :( gibi Oh, ne yapacağız!
Aditya MP

5

Öncelikle cost () işlevinde 30 ve 5 gibi "sihirli sayıları" daha anlamlı değişkenlere değiştirebilirsiniz :)

Ve dürüst olmak gerekirse, bunun için endişelenmemeniz gerektiğini düşünüyorum. Sınıfı değiştirmeniz gerektiğinde değiştireceksiniz. Önce değer yaratmaya çalışın, sonra yeniden düzenleme işlemine geçin.

Ve neden yanlış olduğunu düşünüyorsun? Bu sınıf senin için "yeterince iyi" değil mi? Neden bazı kitaplar için endişeleniyorsun;)


1
Cevabın için teşekkür ederim! Gerçekten de, yeniden düzenleme daha sonra gelecek. Ancak, öğrenme için bu kodu yazdım, kitapta sunulan problemi kendi yolumla çözmeye çalışıyorum ve nasıl yanlış gittiğimi görüyorum ...
Aditya MP

2
Ama bunu daha sonra bileceksin. Bu sınıf sistemin diğer bölümlerinde ne zaman kullanılacak ve yeni bir şey eklemeniz gerekecek. Hiçbir "gümüş kurşun" çözüm :) Bir şey iyi ya da kötü olabilir, sistem, gereksinimleri vb bağlıdır. OOP öğrenirken çok başarısız olmak zorunda: P Sonra zamanla bazı ortak sorunlar ve desenler fark edeceksiniz. Bu örnek bazı tavsiyelerde bulunmak için çok basit. Ohh bu mesajı Joel'den
Michal Franc

@adityamenon O zaman cevabınız "yeniden düzenleme sonra gelecek". Diğer sınıfları yalnızca kodu daha basit hale getirmek için ihtiyaç duyduklarında ekleyin - genellikle, benzer 3. kullanım durumu ortaya çıkar. Simon'un cevabına bakın.
Izkata

3

Herhangi bir ek sınıf uygulamaya kesinlikle gerek yoktur. İşlevinizde biraz MAGIC_NUMBERsorun var cost(), ama hepsi bu. Başka her şey devasa bir aşırı mühendisliktir. Bununla birlikte, ne yazık ki çok kötü tavsiyelerin verilmesi yaygındır - Örneğin, Circle, Shape'i devralır. Bir fonksiyonun basit mirası için bir sınıf elde etmek kesinlikle hiçbir şekilde verimli değildir. Bunun yerine özelleştirmek için işlevsel bir yaklaşım kullanabilirsiniz.


1
Bu ilginç. Bunu nasıl yapacağınızı bir örnekle açıklayabilir misiniz?
guillaume31

+1, katılıyorum, bu kadar küçük bir işlevsellik için aşırı mühendislik / karmaşıklık yapmak için hiçbir neden yok.
GrandmasterB

@ ian31: Özellikle ne yapmalı?
DeadMG

@DeadMG "işlevsel bir yaklaşım kullanın", "kalıtım yerine işlevsel bir uzantı kullanın"
guillaume31
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.