[Edu-sig] Design patterns

Kirby Urner urnerk at qwest.net
Sat Aug 20 02:29:59 CEST 2005


I’ve been thinking that a good way to introduce program design might involve
objects of type Triangle, i.e. instances of the Triangle class.

>>> mytri = Triangle(3,4,5)
>>> mytri.area
6.0
>>> mytri.C
90.0

Now isn’t it true that 3 sides uniquely determine a triangle *except* some
triangles have handedness, and if they're not allowed to flip on the
griddle, you can have two of the same edge lengths (e.g. 3-4-5) that can't
be made to have the same orientation?  We might learn about such asymmetries
by playing Tetris.  The "L" pieces come in two varieties.

Anyway, here's a design question:  do we want to compute the angles and
store them as properties at the time of construction, i.e. in the init?  Or
do we want to make getAngles() a method?  Compromise:  call _getAngles from
the init.

The advantage of always running a method is you always use the a,b and c of
the moment, so if these change e.g. mytri.a = 7, then getAngles() will take
that change into account.  

If angles A,B and C are computed from a, b, c in the constructor and saved
as instance variables, then a subsequent change to b must force a change to
A,B and C, as well as in area and perimeter.  We could do this in more than
one way.  Which way is best?

I think we might go for a class that lets users modify a,b and c as
properties, but doesn't encourage direct access to A,B and C.  Angles are a
result of the sides, but we don't go in the other direction.  Trying to set
mytri.A would raise an exception.  

However, even with sides, we need to patrol for violations of triangular
inequality.  No two lengths are allowed to sum to less than the third
length, and if they exactly equal it, then two of the angles are 0, one is
180, and area is 0.

Here's a version that uses the getAngles() and getArea() approach.  A,B,C
aren't saved as instance variables at all, but as references in a dictionary
that gets returned, but is not saved in the object.  It still needs an
underunder repr.

class Triangle:

    def __init__(self, a,b,c):
        "Construct a triangle"
        if (c > a + b) or (a > b + c) or (b > a + c):
            raise Exception("Illegal edges")
        self.a = a
        self.b = b
        self.c = c

    def getArea(self):
        "Heron's Forumula"
        s = 0.5 * (self.a + self.b + self.c)
        return math.sqrt( s * (s - self.a) * 
                        (s - self.b) * (s - self.c) )
    
    def getAngles(self):
        a,b,c = self.a, self.b, self.c  # for brevity
        angles = {} # empty dictionary
        A = math.acos((-a**2 + b**2 + c**2)/(2.0*b*c) )
        B = math.acos(( a**2 - b**2 + c**2)/(2.0*a*c) )
        C = math.acos(( a**2 + b**2 - c**2)/(2.0*a*b) )
        angles['A'] = round(math.degrees(A),5)
        angles['B'] = round(math.degrees(B),5)
        angles['C'] = round(math.degrees(C),5)
        return angles

Kirby


-- 
No virus found in this outgoing message.
Checked by AVG Anti-Virus.
Version: 7.0.338 / Virus Database: 267.10.13/78 - Release Date: 8/19/2005
 



More information about the Edu-sig mailing list