2016-04-09 17 views
3

Ich bin ein neuer Benutzer (tatsächlich studiert es in einem Kurs) in Smalltalk (Squeak). Ich habe eine Methode, um zu überprüfen, ob ein Rechteck auf ein gegebenes Rechteck entspricht, die wie folgt aussieht:Refactoring eine Methode in Smalltalk

isEqual:givenRec 
    self a = givenRec a 
    ifTrue: [ 
     self b = givenRec b 
     ifTrue: [ 
      ^true 
     ]. 
     ^false 
    ]. 
    self b = givenRec a 
    ifTrue: [ 
     self a = givenRec b 
     ifTrue: [ 
      ^true 
     ]. 
     ^false 
    ]. 
    ^false 

meine Frage ist - gibt es eine Möglichkeit, dies besser zu schreiben? um es kompakter zu machen?

auch - warum kann ich nicht auf a beziehen, die instanceVariableNames mit selbst innerhalb Methoden ist? danke für die Hilfe!

EDIT:

dies ist, wie die Klasse definiert ist:

MyShape subclass: #MyTriangle 
    instanceVariableNames: 'a b c' 
    classVariableNames: '' 
    poolDictionaries: '' 
    category: 'Ex2' 

MyShape leitet sich einfach aus Object und hat nichts.

Antwort

4

Sie können das kompakter machen, ja.

Zuerst gibt es neben #ifTrue: auch #ifFalse: und #ifTrue:ifFalse:, die ungefähr die Wirkung von Wenn-nicht-dann und Wenn-dann-sonst haben.

Aber auch wir haben bereits eine logische UND-Bedingung, also warum nicht nutzen:

isEqual: givenRec 

    (self a = givenRec a and: [self b = givenRec b]) 
     ifTrue: [^ true]. 
    (self b = givenRec a and: [self a = givenRec b]) 
     ifTrue: [^ true]. 
    ^false 

Mit #ifTrue:ifFalse:

isEqual: givenRec 

    (self a = givenRec a and: [self b = givenRec b]) 
     ifTrue: [^ true] 
     ifFalse: [^ (self b = givenRec a and: [self a = givenRec b])] 

Auch können wir die Rückkehr um die ganze Aussage machen:

isEqual: givenRec 

    ^(self a = givenRec a and: [self b = givenRec b]) 
     ifTrue: [true] 
     ifFalse: [self b = givenRec a and: [self a = givenRec b]] 

Aber ifTrue: [true] ist ein bisschen redundant, lassen Sie unsverwenden

isEqual: givenRec 

    ^(self a = givenRec a and: [self b = givenRec b]) or: 
     [self b = givenRec a and: [self a = givenRec b]] 

Schön und süß, wir sehen auch die logische Struktur ziemlich leicht. (Beachten Sie, dass ich von den üblichen Formatierungsstilen abweiche, um auf die Ähnlichkeiten und Unterschiede in den beiden logischen Ausdrücken hinzuweisen).

Wir haben jetzt nur eine Rückkehr ^, kein #ifTrue:…


Für die Instanzvariable Frage:

Wenn Sie einige Instanzvariablen in der Klasse definieren wie Sie haben, können Sie sie buchstäblich verwenden in

Object subclass: #Contoso 
    instanceVariableNames: 'things' 
    classVariableNames: '' 
    poolDictionaries: '' 
    category: 'Examples' 
isThingPlusThreeSameAs: anObject 

    ^thing + 3 = anObject 
0123: Ihr Code auf sie zuzugreifen

Aber in der Regel ist es besser, über Getter zu Instanzvariablen beziehen und Setter, allgemein Accessoren genannt.Sie müssen sie manuell schreiben oder den Menüpunkt des Sekunden Kontextmenü ‚inst var Accessor erstellen‘ verwenden (über „mehr ...“) einer Klasse im Browser:

Diese wird Accessormethoden der

thing 

    ^thing 
Form erzeugen
thing: anObject 

    thing := anObject 

und Sie können sie in othe verwenden r Methoden wie dies

isThingPlusThreeSameAs: anObject 

    ^self thing + 3 = anObject 
+0

Dank, herausgegeben ich meine Frage - hat der Klassendefinition – Infested

+0

eine andere Frage @Tobias - Wie kann ich zeichnen ein Rechteck auf dem Bildschirm angesichts seiner zwei Kantenlängen? – Infested

+0

So sieht Ihre Klasse gut aus, es kommt jetzt wirklich auf den Code an, den Sie für den Vergleich hatten. ~~ Bitte machen Sie eine neue Frage für die Instanz Variable Frage ~~ BEARBEITEN Sie meine Antwort – Tobias

0

Ich hätte das Verfahren als

equals: aTriangle 
    a = aTriangle a ifFalse: [^false]. 
    b = aTriangle b ifFalse: [^false]. 
    ^c = aTriangle c 

Hinweis durch die Art und Weise geschrieben, dass ich mit dem Selektor #equals: anstatt #isEqual:, weil diese nicht so gut liest in Englisch (wäre es gewesen wäre #isEqualTo:, aber #equals: ist kürzer).

Eine weitere Bemerkung, die ich hier hinzufügen möchte, ist, dass Sie #= neu definiert haben könnten, anstatt einen neuen Vergleichsselektor hinzuzufügen. In diesem Fall Sie haben jedoch die Aufmerksamkeit auf ein paar zusätzliche Dinge zu bezahlen:

= aTriangle 
    self class = aTriangle class ifFalse: [^false]. 
    a = aTriangle a ifFalse: [^false]. 
    b = aTriangle b ifFalse: [^false]. 
    ^c = aTriangle c 

Der Grund für die Klasse überprüfen, um sicherzustellen, dass #= robust ist, das heißt, es nicht ein MessageNotUnderstood Ausnahmesignal . Die andere Sache ist, dass jedes Mal, wenn Sie (neu) definieren Sie auch #hash so definieren, dass t1 hash = t2 hash wann immer t1 = t2. Zum Beispiel

hash 
    ^(a hash + b hash + c hash) hash 

Beachten Sie, dass dieser Vorschlag für #hash ist nicht die beste, aber das ist nicht der Ort, um das Problem des Schreibens gut #hash Funktionen zu diskutieren.

UPDATE

Hier ist meine Version, wenn der Auftrag

equals: aTriangle 
    | sides | 
    self = aTriangle ifTrue: [^true]. 
    sides := Set with: a with: b with: c. 
    (sides includes: aTriangle a) ifFalse: [^false]. 
    (sides includes: aTriangle b) ifFalse: [^false]. 
    ^(sides includes: aTriangle c) 

Der erste Vergleich spielt keine Rolle, ist die Methode der Set vermeiden zu beschleunigen, wenn die Reihenfolge ist das gleiche.

+0

Sie können die frühe Rückkehr nicht verwenden, wenn ein Scheitelpunkt sich unterscheidet, weil er Eckpunkte vergleichen möchte, die die Reihenfolge ignorieren, also (a, b, c) = (c , b, a) –

+0

@ CarlosE.Ferro Danke, ich habe die Antwort aktualisiert. –

+0

Ich mag es nicht, zwei verschiedene Begriffe der Gleichheit zu haben, einen mit geordneten Scheitelpunkten in # = und den anderen mit der Menge in #equalsTo: .. –

0

Sie sollten wahrscheinlich schreiben Sie es nicht so, aber es gibt Situationen, in denen Meta-Programmierung dies nützlich

isEqual: givenRec 
    #(a b c) do: [ :selector | 
    (self perform: selector) = (givenRec perform: selector) ifFalse: [ 
     ^false]]. 
    ^true