C#. Types of relationships between classes: is-a, has-a, uses. Examples. Aggregation. Composition

Types of relationships between classes: is-a, has-a, uses. Examples. Aggregation. Composition


Contents


1. What types of relationships exist between classes?

Two types of relationships are possible between classes:

  • 1. Relationship type is-a (is-a relationship). In this case, one class is a subspecies of another class. In other words, one class expands the capabilities of another class. This type is based on the use of the inheritance mechanism.
  • 2. A relations in which there is a relationship between two classes. This relationship is divided into two subtypes:
    • 2.1. Relationship of type has-a (has-a relationship). In this case, one or more objects of another class are declared in the class. There is also a division: aggregation, composition. If nested objects can exist independently of the class (they are not an integral part of the class), then this is aggregation. If nested objects (an object) supplement the class in such a way that the existence of a class is inconceivable without these objects, then this is a composition or union;
    • 2.2. A relation of type uses (class “uses a different class). This is a generalized relation in which different forms of using one class by another are possible. If two classes are declared in a program, then optionally one class must contain an instance of another class. A class can use only some method of another class, a class can access the name of another class (use the name), a class can use the data field of another class, etc. You can read more about using the relationship type uses here.

 

2. An example of the simplest type of is-a relationship (inheritance)

The example demonstrates the implementation of the is-a relationship. Such relationship is necessary when it is necessary to modify (expand) an existing program code (class).

Let a class Point be defined that describes a point on the coordinate plane. The following items are implemented in the class:

  • internal fields x, y;
  • constructor with two parameters;
  • a constructor without parameters that initialize the class fields with coordinates (0; 0);
  • X, Y properties to access the internal fields x, y of the class;
  • the LengthOrigin() method, which determines the length from the point (x; y) to the origin;
  • method Print(), which displays the value of the fields x, y.

The next step is the need to extend the Point class with a new color element that defines the color of a point on the coordinate plane. In order not to correct the code (sometimes this is impossible) of the Point class, it is enough to implement the new ColorPoint class, which inherits (extends) the Point class and adds color to it.

In this example, the inherited ColorPoint class implements elements that complement (extend) the capabilities of the Point class:

  • internal hidden field color – color of the point, which is obtained from the Colors enumeration;
  • constructor with 3 parameters, initializing the value of the point with coordinates (x; y) and color value;
  • property Color that implements access to the internal color field;
  • The Print() method, which displays the color value and x, y coordinates of the Color base class. The method calls the Print() method of the base class of the same name.

The text of the program is as follows.

using System;
using static System.Console;

namespace ConsoleApp1
{
  // An enumeration defining a color palette,
  // required for use in the ColorPoint class
  enum Colors { Black = 0, Green = 1, Yellow = 2, Red = 3, Blue = 4 };

  // Base class Point
  class Point
  {
    // 1. Internal fields of class - coordinates x, y
    private double x, y;

    // 2. Class constructors
    // 2.1. Constructor with 2 parameters - main constructor
    public Point(double x, double y)
    {
      this.x = x; this.y = y;
    }

    // 2.2. Constructor without parameters
    public Point() : this(0, 0)
    {
    }

    // 3. Properties X, Y
    public double X
    {
      get { return x; }
      set { x = value; }
    }

    public double Y
    {
      get { return y; }
      set { y = value; }
    }

    // 4. Method LengthOrigin()
    public double LengthOrigin()
    {
      // Pythagorean theorem
      return Math.Sqrt(x * x + y * y);
    }

    // 5. Method Print()
    public void Print()
    {
      WriteLine($"x = {x}, y = {y}");
    }
  }

  // Inherited class ColorPoint
  class ColorPoint : Point
  {
    // 1. Hidden field - the color of point
    private Colors color;

    // 2. Constructor with 3 parameters
    public ColorPoint(double x, double y, Colors color) : base(x, y)
    {
      this.color = color;
    }

    // 3. Property Color
    public Colors Color
    {
      get { return color; }
      set
      {
        if (color >= 0)
          color = value;
        else
          color = 0;
      }
    }

    // 4. Method Print()
    void Print()
    {
      // Invoke method of base class
      base.Print();

      // Display color
      Write("color = ");
      switch(color)
      {
        case Colors.Black:
          WriteLine("Black");
          break;
        case Colors.Blue:
          WriteLine("Blue");
          break;
        case Colors.Green:
          WriteLine("Green");
          break;
        case Colors.Red:
          WriteLine("Red");
          break;
        case Colors.Yellow:
          WriteLine("Yellow");
          break;
      }
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // Demonstration of Point class
      WriteLine("Demo Point:");
      Point pt = new Point(3, 5);
      pt.Print();
      double len = pt.LengthOrigin();
      WriteLine("LengthOrigin = {0:f2}", len);

      // Demonstration of ColorPoint class
      WriteLine("Demo ColorPoint");
      ColorPoint cp = new ColorPoint(1, 3, Colors.Green);
      cp.Print();
    }
  }
}

The result of the program

Demo Point:
x = 3, y = 5
LengthOrigin = 5.83
Demo ColorPoint
x = 1, y = 3

 

3. Relationship between classes of has-a type

With a has-a relationship, a class contains one or more objects (instances) of another class. There are two varieties of a has-a relationship:

  • aggregation. This is the case when one or more nested objects is not part of the class, that is, the class can exist without these objects. A class can contain any number of such objects (even 0). See the examples below;
  • composition. In this case, one or more nested objects is part of the class, that is, without these objects, the logical existence of the class itself is impossible.

Examples of classes in which the aggregation approach is implemented:

  • the CarPark class can contain arrays (lists) of instances of the classes Car, Motorcycle, Bus. If at any moment in time there will not be a single car in the parking lot, the parking lot will continue to function;
  • the Figures class can contain arrays of instances of the classes Rectangle, Triangle, Circle;
  • the House class can contain a different number of objects of the Table, TVSet, Bed classes, etc.

Examples of interactions between classes that relate to composition:

    • the Car class must contain one instance of the Engine class and four instances of the Wheel class. Instances of the Engine and Wheel classes are an integral part of the Car. If you remove one of these instances, the Car will not function and, as a result, the Car class will not work;
  • the House class must contain an instance of the Roof class and four instances of the Wall class;
  • the Triangle class (a triangle on the coordinate plane) contains three instances of the Point class.


 

3.1. Aggregation for has-a relationship type. Example

In the case of aggregation, a class contains many (one or more) objects of other classes that are not part of this class.

Example. The Figures class contains an array of Point classes and an array of Line classes. The number of elements in arrays can be arbitrary, even equal to 0. This means that the Figures class can exist without existing instances of the Point or Line classes. This type of interaction between classes is called aggregation.

The text of the demo example is as follows.

using System;
using static System.Console;

namespace ConsoleApp1
{
  // Aggregation
  // 1. Class that describes the point
  class Point
  {
    // Internal fields of class
    public double x;
    public double y;
  }

  // 2. Class that describes a line
  class Line
  {
    public Point pt1 = null;
    public Point pt2 = null;
  }

  // 3. A class that describes an array of figures
  class Figures
  {
    // 1. Internal fields of class
    public Point[] points; // array of points
    public Line[] lines; // array of lines

    // 2. Class constructor
    public Figures()
    {
      points = null;
      lines = null;
    }

    // 3. Method for displaying array items on the screen
    public void Print()
    {
      WriteLine("Array points:");
      for (int i = 0; i < points.Length; i++)
      {
        WriteLine("x = {0}, y = {1}", points[i].x, points[i].y);
      }

      WriteLine("Array lines:");
      for (int i=0; i<lines.Length; i++)
      {
        WriteLine("pt1.x = {0}, pt1.y = {1}", lines[i].pt1.x, lines[i].pt1.y);
        WriteLine("pt2.x = {0}, pt2.y = {1}", lines[i].pt2.x, lines[i].pt2.y);
      }
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // Demonstration of aggregation using the Figures, Point, Line Classes
      // 1. Create an instance of Figures class
      Figures fg = new Figures();

      // 2. Create an array of 5 points, which are objects of class Point
      //   of instance of Figures class
      // 2.1. Allocate memory for 5 array items
      fg.points = new Point[5];

      // 2.2. Allocate memory for each array item
      //      and fill it
      for (int i = 0; i < fg.points.Length; i++)
      {
        fg.points[i] = new Point();
        fg.points[i].x = i * i;
        fg.points[i].y = i * i * i;
      }

      // 3. Create an array of 3 lines
      // 3.1. Allocate memory for 3 array items
      fg.lines = new Line[3];

      // 3.2. Allocate memory for each array item
      //      and fill it
      for (int i = 0; i < fg.lines.Length; i++)
      {
        fg.lines[i] = new Line();
        fg.lines[i].pt1 = new Point();
        fg.lines[i].pt2 = new Point();
        fg.lines[i].pt1.x = i;
        fg.lines[i].pt1.y = i * 2;
        fg.lines[i].pt2.x = i * 3;
        fg.lines[i].pt2.y = i * i;
      }

      // 4. Display an array of points and lines on the screen
      fg.Print();
    }
  }
}

The result of the program

Array points:
x = 0, y = 0
x = 1, y = 1
x = 4, y = 8
x = 9, y = 27
x = 16, y = 64
Array lines:
pt1.x = 0, pt1.y = 0
pt2.x = 0, pt2.y = 0
pt1.x = 1, pt1.y = 2
pt2.x = 3, pt2.y = 1
pt1.x = 2, pt1.y = 4
pt2.x = 6, pt2.y = 4

 

3.2. Composition for has-a relationship type. Example

Consider the Line class, which describes a line based on two points. Points are described by the Point class. The Line class contains 2 instances of the Point classes. Without these instances (objects), the Line class cannot exist, since both instances form part of the line (extreme points of the line). Thus, both instances of the Point class are part of the Line class. This type of interaction is called a composition or a union.

A fragment of the example is as follows.

...

// Composition
// 1. Class that describes a point
class Point
{
  // Internal fields
  public double x;
  public double y;
}

// 2. A fragment of the example is as follows.
class Line
{
  // The internal fields of a class are instances (objects) of the Point class.
  // Without these fields, the Line class does not make sense, which means
  // the pt1, pt2 fields complement the Line class (is part of the Line class),
  // it is a composition.
  public Point pt1 = null;
  public Point pt2 = null;
}

...

 


Related topics