0% found this document useful (0 votes)
2 views8 pages

Chapter 18

Chapter 18 provides an overview of advanced object-oriented programming (OOP) principles, including abstraction, encapsulation, inheritance, and polymorphism, essential for understanding the module. It emphasizes the importance of exception handling, coding conventions, and the use of Visual Studio 2022 for project setup. The chapter also includes examples illustrating these concepts, such as class diagrams and method implementations, to enhance comprehension of OOP principles.

Uploaded by

brightmohlala06
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views8 pages

Chapter 18

Chapter 18 provides an overview of advanced object-oriented programming (OOP) principles, including abstraction, encapsulation, inheritance, and polymorphism, essential for understanding the module. It emphasizes the importance of exception handling, coding conventions, and the use of Visual Studio 2022 for project setup. The chapter also includes examples illustrating these concepts, such as class diagrams and method implementations, to enhance comprehension of OOP principles.

Uploaded by

brightmohlala06
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 8

1

Chapter 18
Advanced OO principles

1. Introduction
This chapter follows on Chapters 11 and 12. You should be fully conversant with the content of those
chapters as well as Nakov Chapters 11 and 14 before proceeding.

This chapter is not intended to be a complete discussion of OO principles but only a short overview
of some of the principles that are needed for the rest of this module.

This chapter is largely based on Nakov Chapters 11, 14 and 20 and is acknowledged.

2. Some of the Basic Principles of OOP


Both abstraction and encapsulation in object oriented programming refer to the concept of hiding
details. Abstraction does so at the design level while encapsulation hides details at the implementation
level.

Abstraction refers to the mechanism and practice through which real-world things are modeled in
order to solve complex problems. Details are reduced and factored out so that one can focus on a few
concepts at a time. Objects are identified and organised into a class hierarchy. We focus on one class
at at time and when there is interaction between classes, we assume that the details in the other classes
are OK and just work with what they offer through their public members. We don't deal with lower-
level classes until we are done with the upper-level ones. When we work with the lower-level classes,
we assume that everything is OK in the upper-level classes and focus our attention on the details in
the lower-level classes.

Encapsulation refers to the fact that we hide details from a class user so that they can focus on the
usage thereof without worrying about how it works. For example, we can use the built-in .Net method
Array.Sort easily, but we do not know what algorithm Microsoft uses to do the sorting and we do
not worry about it – as long as it does its job correctly and efficiently. This sounds like abstraction,
but the difference lies in the fact that with abstraction we can access details if we want to whereas
with encapsulation, details are totally inaccessible.

Inheritance is a way to form new classes while using classes that have already been defined. It is
employed to reuse existing code with little or no modification. The derived classes inherit attributes
(properties) and behaviour (methods) of the existing classes.

Polymorphism refers to the ability of objects belonging to different classes to respond to a method,
field, or property calls of the same name. The programmer does not have to know the exact class of
the object in advance, and the exact behaviour is determined at run-time (this is called late or dynamic
binding).

Copyright: PJ Blignaut, 2020


2

3. Exception handling
Run-time errors if a user makes a mistake are unforgiveable! Users are human and humans make
mistakes. You can never blame a user for a run-time error – the programmer should have anticipated
the possibility. Therefore, testing with representative data of all possible scenarios is essential.

• Refrain from using try{ } catch{ } blocks as far as possible. Rather use TryParse when
possible. If you have to use try{ } catch{ }, never do so without proper feedback to the user in
case of an error. Use the Exception parameter and display the exception message if the message
will not be too cryptic for end users:

private static Makes GetMake(string prompt)


{
try
{
Console.Write("\t" + prompt);
string sMake = Console.ReadLine();
return (Makes)Enum.Parse(typeof(Makes), sMake);
}
catch (Exception ex)
{
Console.WriteLine("\t" + ex.Message);
return GetMake(prompt);
}
} //GetMake

• Make sure that you do not cast objects to an invalid type. See Section 5.2 below for examples. Use
the is operator to check that an object is a member of a specific class if there might be doubt.
• Most common errors:
- Operations on an object with a null value. Ensure that your objects are properly instantiated.
- Index out of bounds
- Non-existing files. Always use (if (File.Exists(…){ } )
• NB NB: Use the debugging tools that are available! I cannot see how it is possible to code without
extensive use of breakpoints and stepping through code while inspecting the values of variables.
• When testing, do so with representative data. Create a data set with instances that lie on and on
both sides of border cases. Ask a friend to try to break your program and give them an incentive if
they succeed in doing so.

4. Conventions
Abiding by conventions promotes maintainability and readability of code.

• Class names should start with capital "C", for example CCar. That also holds for forms, e.g.
CfrmMain.
• Interface names should start with "I".
• Every class should go in a separate file. Enums and interfaces should also go in separate files, but
you may group enums together in a single file.
• Every file and thus effectively every class and interface, must be annotated at the top with the
name of the programmer, date of last update and a short indication of purpose. Also add copyright
information.
• Class diagrams: See Chapter 12, p 234
• This is not so important, but it irritates me if there are some unused using directives at the top of a
file. Right-click on the code and click "Remove and Sort Usings".
Copyright: PJ Blignaut, 2020
3

5. Notes on VS 2022

If you use VS 2022, please:

• When starting a new project,


- project type Console app or Windows Forms App with C#
- Framework: .Net 4.5 or .Net 6.0 (Long-term support)
- For console apps, select "Do not use top-level statements"

• Project / Edit Project File: Change ImplicitUsings to disable and Save

6. Learning by Example
An example from Nakov Chapter 20 is taken with some more explanations. The full program is
available in Listing 18. Some extracts are provided here to highlight the topics as they are discussed.

6.1 Setting the scene

Consider the following class diagram. This will form the basis for the examples in this chapter.
Remember that the arrows should be read as "is a" which means that a member of CElephant is also
a member of CAnimal. CElephant inherits from CAnimal and not the other way round. So an African
lion is a lion is a cat (CFelidae) is an animal.

CElephant
+IsMale

CLion
-backLeft
CFelidae -backRight
-frontLeft CAfricanLion
+Gender
CAnimal -frontRight
+CatchPrey +Weight +CatchPrey
+Hide +Roar
+Ambush
+Run +ToString
+CatchPrey
+Walk
InitPaws
+Reproduce
+Walk

CGoat

+ToString
Class diagram for the examples in this chapter
(With acknowledgement to Nakov Chapter 20)

6.2 Casting from sub-class to a parent class and vice versa

Since a member of a subclass is a member of a parent class, we can instantiate an instance of a sub-
class and assign it to an object of a parent class but not the other way round. This instance is still a
member of the sub-class and its properties and methods are still available if we cast it down to the
subclass.

CLion AfrLion1 = new CAfricanLion(Gender.Male, 20);


lion.Roar(); //Error here because the method Roar is not defined for CLion
((CAfricanLion)AfrLion1).Roar(); //Now it works

Copyright: PJ Blignaut, 2020


4

We can also do an upward implicit casting, although it is dangerous without checking first (see below).

CAfricanLion AfrLion2 = new CAfricanLion(Gender.Male, 40);


CLion lion = AfrLion2;

But this is illegal:


CLion Lion3 = new CLion(Gender.Male, 40);
CAfricanLion lion3 = Lion3; //Try to downcast implicitly - Error

If we cast explicitly, we might run into trouble. We can, for example, not cast an elephant into a lion.
We have to check if it is possible:
object elephant1 = new CElephant();
if (elephant1 is CAfricanLion)
{
CAfricanLion AfrLion4 = (CAfricanLion)elephant1; //Will not be done
}

if (elephant1 is CElephant)
{
CElephant elephant2 = (CElephant)elephant1; //Will be done
}

6.3 ToString

All classes inherit from object which has a built-in ToString method. If we use it as-is, it will
return the class name of the object that was created. The output of the following statements are shown
in comments next to the statement:

Console.WriteLine("1. " + new object()); //Prints "System.Object"


Console.WriteLine("2. " + new CLion(Gender.Male, 80)); //Prints "[namespace].CLion"

The CAfricanLion class has its own ToString method which overrides that of object.

In the class:
public override string ToString()
{
return string.Format( "(AfricanLion, {0}, {1} kg)", this.Gender, this.Weight);
}

When called:
CAfricanLion africanLion = new CAfricanLion(Gender.Male, 80);
Console.WriteLine("3. " + africanLion.ToString()); //Prints "(AfricanLion, Male, 80 kg)
Console.WriteLine("4. " + africanLion); //Prints "(AfricanLion, Male, 80 kg)

If the keyword override above is replaced with new, the base class method still exists. There are now
two versions of ToString and the version in the subclass will only be used if it is explicitly called:

In the class:
public new string ToString()
{
return string.Format( "(AfricanLion, {0}, {1} kg)", this.Gender, this.Weight);
}

When called:
CAfricanLion africanLion = new CAfricanLion(Gender.Male, 80);
Console.WriteLine("3. " + africanLion.ToString()); //Prints "(AfricanLion, Male, 80 kg)
Console.WriteLine("4. " + africanLion); //Prints "[namespace].CAfricanLion)

Copyright: PJ Blignaut, 2020


5

6.4 Transitivity

CAfricanLion does not have properties for Weight and Gender. Weight is defined in its parent class
CLion and Gender in the parent class of CLion, namely CFelidae (See the class diagram for clarity).

When the constructor for CAfricanLion is called,

CAfricanLion africanLion = new CAfricanLion(Gender.Male, 80);

the parameters are passed through to its parent class, CLion:

public CAfricanLion(Gender gender, int weight) : base(gender, weight)


{

The constructor of CLion will assign the weight and in turn pass the gender through to the constructor
of CFelidae:

public CLion(Gender gender, int weight) : base(gender)


{
this.weight = weight;
} //CLion

The constructor of CFelidae will (at last) assign the gender:

public CFelidae(Gender gender)


{
this.gender = gender;
} //Constructor

When the properties of africanLion are inspected with IntelliSense, both Gender and Weight will
be available. If, however, the instance is cast to CFelidae, only Gender is available:

This is also an example of the principle of abstraction: We can focus on the properties of CFelidae
and leave the details of CAfricanLion until we are ready for it.

6.5 Interfaces

An interface is an abstract class that can be inherited by sub-classes. By convention the interface
name starts with 'I'. An interface lists a number of methods and properties without implementation.
All members are by default public and no access modifiers can be specified. All members must be
implemented by the class that inherits from it.

Copyright: PJ Blignaut, 2020


6

In our example, IReproducable contains a method signature, Mate, that takes a parameter of generic
class T and returns an array of T. T is limited to be CFelidae or one of its sub-classes. In effect, this
means that a lion will be able to mate with a house cat (!!!) but not with an elephant.

interface IMating<T> where T : CFelidae


{
T[] Mate(T mate);
}

CLion inherits from IReproducable and implements the method Mate by returning a pair of lions
that includes the current instance (this) and another one that is provided through a parameter.

class CLion : CFelidae, IMating<CLion>


{
...
public CLion[] Mate(CLion mate)
{
return new CLion[] { this, mate };
} //Reproduce
...
}

The Mate method can be called by an instance of CLion:

CLion lion1 = new CLion(Gender.Male, 80);


CLion lion2 = new CLion(Gender.Female, 70);
CLion[] lionPair = lion1.Mate(lion2);

Console.WriteLine("Gender\tWeight");
Console.WriteLine("====\t======");
foreach (CLion lion in lionPair)
Console.WriteLine("{0}\t{1}", lion.Gender, lion.Weight);

6.6 Encapsulation

Encapsulation refers to the fact that we provide a clear interface to a class user while hiding the details
thereof. In our example, the method Walk is public, but the details of how the method is implemented,
is private. In this example, the CLion class contains a private class, Paw that specifies walking is done
by incrementing the coordinates of a paw. Four instances of this class is instantiated and the method
Walk specifies the sequence in which each one of the four paws is advanced.

class CLion : CFelidae, IMating<CLion>


{
//Private class
private class Paw
{
private Point position;

public Paw(Point position)


{
this.position = position;
}

//Method with details about movement of a paw


public void MoveForward()
{
position.Y++;
}
} //class Paw

Copyright: PJ Blignaut, 2020


7

//Paw objects
private Paw frontLeft, frontRight, backLeft, backRight;

//Constructor
public CLion(Gender gender, int weight) : base(gender)
{
frontLeft = new Paw(new Point(2, 3));
frontRight = new Paw(new Point(3, 4));
backLeft = new Paw(new Point(2, 6));
backRight = new Paw(new Point(3, 7));
} //Constructor

public override void Walk()


{
this.frontLeft.MoveForward();
this.frontRight.MoveForward();
this.backLeft.MoveForward();
this.backRight.MoveForward();
} //Walk
} //class CLion

Listing 18: Excerpt from the CLion class to illustrate encapsulation. Details about other
operations have been removed.

The class user has nothing to do with all the details above and has access to the Walk method only.
CLion lion = new CLion(Gender.Male, 100);
lion.Walk(); //Walk is public, but the methods called from Walk are private

6.7 Polymorphism

Polymorphism refers to the ability of an object to determine at run-time to which class it belongs and
call a method from the correct class.

In our example, CFelidae has an abstract method (signature only), CatchPrey:

public abstract void CatchPrey(object prey);

Both classes CLion and CAfricanLion inherit from CFelidae but each one has its own implementation
of the method CatchPrey.
class CLion : CFelidae, ...
{
public override void CatchPrey(object prey)
{
... Do something
}
} //CLion

class CAfricanLion : CLion


{
public override void CatchPrey(object prey)
{
... Do something

//Calling the base method as well


base.CatchPrey(prey);
} //CatchPrey
} //CAfricanLion

Copyright: PJ Blignaut, 2020


8

When an object in this hierarchy, CAfricanLion : CLion : CFelidae, is instantiated, the object must
know which one of the CatchPrey methods to call. This ability is polymorphism: more than one
(poly) forms (morphism) of the same method. Note that having different methods in the subclasses
with the same name does not in itself mean polymorphism – the methods must inherit from an abstract
or virtual method in a common base class.

CFelidae lion = new CLion(Gender.Female, 70);


CAfricanLion AfricanLion = new CAfricanLion(Gender.Female, 70);
lion.CatchPrey( new CGoat() ); //Will call the method in CLion
AfricanLion.CatchPrey(new CGoat()); //Will call the method in CAfricanLion

Since an African lion is a lion (CAfricanLion : CLion), the CatchPrey method in CAfricanLion may
optionally call CatchPrey in CLion. This means that CatchPrey in CAfricanLion effectively executes
both methods.

7. Summary
The following key concepts were discussed in this chapter:

• Abstraction
• Encapsulation
• Inheritance
• Polymorphism
• Exception handling
• Debugging tools
• Most common errors
• Conventions
• Casting between classes
• override and new ToString
• Transitivity
• Interfaces

Copyright: PJ Blignaut, 2020

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy