{ |one, step, back| }

The Shape Example in Modula3

Contributed by Chris Rathman

Code for Modula3

File: Shape.i3

INTERFACE Shape;
   TYPE
      T <: Public;
      Public = ROOT OBJECT
         METHODS
            draw();
            moveTo(newx: INTEGER; newy: INTEGER);
            rMoveTo(deltax: INTEGER; deltay: INTEGER);
            getX(): INTEGER;
            getY(): INTEGER;
      END;
END Shape.

File: Shape.m3

MODULE Shape;

   REVEAL
      T = Public BRANDED OBJECT
         x: INTEGER;
         y: INTEGER;
         METHODS
            setX(newx: INTEGER) := SetX;
            setY(newy: INTEGER) := SetY;
         OVERRIDES
            moveTo := MoveTo;
            rMoveTo := RMoveTo;
            getX := GetX;
            getY := GetY;
      END;

   (* accessors for x & y *)
   PROCEDURE GetX(self: T): INTEGER =
   BEGIN
      RETURN self.x;
   END GetX;

   PROCEDURE GetY(self: T): INTEGER =
   BEGIN
      RETURN self.y;
   END GetY;

   PROCEDURE SetX(self: T; newx: INTEGER) =
   BEGIN
      self.x := newx;
   END SetX;

   PROCEDURE SetY(self: T; newy: INTEGER) =
   BEGIN
      self.y := newy;
   END SetY;

   (* move the shape position *)
   PROCEDURE MoveTo(self: T; newx: INTEGER; newy: INTEGER) =
   BEGIN
      self.setX(newx);
      self.setY(newy);
   END MoveTo;

   PROCEDURE RMoveTo(self: T; deltax: INTEGER; deltay: INTEGER) =
   BEGIN
      self.moveTo(self.getX() + deltax, self.getY() + deltay);
   END RMoveTo;

BEGIN
END Shape.

File: Rectangle.i3

INTERFACE Rectangle;
   IMPORT Shape;
   TYPE
      T <: Public;
      Public = Shape.T OBJECT
         METHODS
            init(x: INTEGER; y: INTEGER; width: INTEGER; height: INTEGER): T;
            getWidth(): INTEGER;
            getHeight(): INTEGER;
            setWidth(newwidth: INTEGER);
            setHeight(newheight: INTEGER);
      END;
END Rectangle.

File: Rectangle.m3

MODULE Rectangle;
   IMPORT IO;
   IMPORT Fmt;

   REVEAL
      T = Public BRANDED OBJECT
         width: INTEGER;
         height: INTEGER;
         OVERRIDES
            init := Init;
            getWidth := GetWidth;
            getHeight := GetHeight;
            setWidth := SetWidth;
            setHeight := SetHeight;
            draw := Draw;
      END;

   (* Initialize the attributes of the object *)
   PROCEDURE Init(self: T; x: INTEGER; y: INTEGER;
      width: INTEGER; height: INTEGER): T =
   BEGIN
      self.moveTo(x, y);
      self.setWidth(width);
      self.setHeight(height);
      RETURN self;
   END Init;

   (* accessors for width & height *)
   PROCEDURE GetWidth(self: T): INTEGER =
   BEGIN
      RETURN self.width;
   END GetWidth;

   PROCEDURE GetHeight(self: T): INTEGER =
   BEGIN
      RETURN self.height;
   END GetHeight;

   PROCEDURE SetWidth(self: T; newwidth: INTEGER) =
   BEGIN
      self.width := newwidth;
   END SetWidth;

   PROCEDURE SetHeight(self: T; newheight: INTEGER) =
   BEGIN
      self.height := newheight;
   END SetHeight;

   (* draw the rectangle *)
   PROCEDURE Draw(self: T) =
   BEGIN
      IO.Put('Drawing a Rectangle at:(' &
         Fmt.Int(self.getX()) &  ',' &
         Fmt.Int(self.getY()) & '), width ' &
         Fmt.Int(self.getWidth()) & ', height ' &
         Fmt.Int(self.getHeight()) & '\n');
   END Draw;

BEGIN
END Rectangle.

File: Circle.i3

INTERFACE Circle;
   IMPORT Shape;
   TYPE
      T <: Public;
      Public = Shape.T OBJECT
         METHODS
            init(x: INTEGER; y: INTEGER; radius: INTEGER): T;
            getRadius(): INTEGER;
            setRadius(newradius: INTEGER);
      END;
END Circle.

File: Circle.m3

MODULE Circle;
   IMPORT IO;
   IMPORT Fmt;

   REVEAL
      T = Public BRANDED OBJECT
         radius: INTEGER;
         OVERRIDES
            init := Init;
            getRadius := GetRadius;
            setRadius := SetRadius;
            draw := Draw;
      END;

   (* Initialize the attributes of the object *)
   PROCEDURE Init(self: T; x: INTEGER; y: INTEGER;
      radius: INTEGER): T =
   BEGIN
      self.moveTo(x, y);
      self.setRadius(radius);
      RETURN self;
   END Init;

   (* accessors for radius *)
   PROCEDURE GetRadius(self: T): INTEGER =
   BEGIN
      RETURN self.radius;
   END GetRadius;

   PROCEDURE SetRadius(self: T; newradius: INTEGER) =
   BEGIN
      self.radius := newradius;
   END SetRadius;

   (* draw the circle *)
   PROCEDURE Draw(self: T) =
   BEGIN
      IO.Put('Drawing a Circle at:(' &
         Fmt.Int(self.getX()) & ',' &
         Fmt.Int(self.getY()) & '), radius ' &
         Fmt.Int(self.getRadius()) & '\n');
   END Draw;

BEGIN
END Circle.

File: Main.m3

MODULE Main EXPORTS Main;
   IMPORT Shape;
   IMPORT Rectangle;
   IMPORT Circle;

VAR
   scribble: ARRAY[1..2] OF Shape.T;
   rect : Rectangle.T;

BEGIN
   (* set up some shape instances *)
   scribble[1] := NEW(Rectangle.T).init(10, 20, 5, 6);
   scribble[2] := NEW(Circle.T).init(15, 25, 8);

   (* iterate through some shapes and handle polymorphically *)
   FOR i := 1 TO 2 DO
      scribble[i].draw();
      scribble[i].rMoveTo(100, 100);
      scribble[i].draw();
   END;

   (* access a rectangle specific function *)
   rect := NEW(Rectangle.T).init(0, 0, 15, 15);
   rect.setWidth(30);
   rect.draw();
END Main.

File: m3makefile

% Makefile for Modula-3 Shape polymorphism program
import('libm3')
module('Shape')
module('Rectangle')
module('Circle')
implementation(Main)
program(Main)

Output

Drawing a Rectangle at:(10,20), width 5, height 6
Drawing a Rectangle at:(110,120), width 5, height 6
Drawing a Circle at:(15,25), radius 8
Drawing a Circle at:(115,125), radius 8
Drawing a Rectangle at:(0,0), width 30, height 15