0% found this document useful (0 votes)
4 views43 pages

07 Working Classes

Chapter 07 discusses the importance of object-oriented programming, focusing on classes as Abstract Data Types (ADTs) that help manage complexity in software development. It emphasizes good class design principles, such as encapsulation, abstraction, and the preference for containment over inheritance. The chapter also outlines common pitfalls to avoid, such as creating 'God Classes' and maintaining clean interfaces.

Uploaded by

eaindraychu32
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)
4 views43 pages

07 Working Classes

Chapter 07 discusses the importance of object-oriented programming, focusing on classes as Abstract Data Types (ADTs) that help manage complexity in software development. It emphasizes good class design principles, such as encapsulation, abstraction, and the preference for containment over inheritance. The chapter also outlines common pitfalls to avoid, such as creating 'God Classes' and maintaining clean interfaces.

Uploaded by

eaindraychu32
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

Chapter 07: Working Classes

Building High-Quality Object-Oriented Structures


Agenda
01 02

The Evolution of Programming Paradigms Class Foundations: Abstract Data Types (ADTs)

03 04

Designing Good Class Interfaces Encapsulation vs. Abstraction

05 06

Design and Implementation: Containment & Inheritance Deep vs. Shallow Copies

07 08

Reasons to Create a Class Classes to Avoid


Evolution of Software
Composition
1 1950s – 1960s
Programmers thought in terms of individual statements.

2 1970s – 1980s
Programmers started thinking in terms of routines
(functions/procedures).

3 21st Century
Programmers now think in terms of classes.

Goal: Maximizing the portion of a program that you can safely ignore while
working on any one section of code.
What is a Class?
Definition
A class is a collection of data and routines that share a cohesive, well-
1 defined responsibility.

It acts as the primary tool for managing complexity in modern software


development.

The Cookie Cutter Analogy


2 Classes are the blueprints (cookie cutters); objects are the specific
entities created from them (the cookies).
6.1 Class Foundations: ADTs
What is an ADT?

ADT Definition Loose Use of "Data" Foundation of OOD


Abstract Data Type: A collection of data The word "data" is used loosely. An ADT Thinking about ADTs first and classes
and operations that work on that data. could be a graphics window, a file, or an second is the foundation of solid Object-
insurance-rates table. Oriented Design.
Benefits of Using ADTs

Hide Implementation Details Limit Changes


You don't need to know how the data is stored (e.g., array vs. linked Changes don't propagate throughout the whole program.
list).

Self-Documenting Code Improve Performance


ADT interfaces make the code more readable and self-explanatory. You can optimize the internal implementation later without rewriting
the rest of the program.
ADT Example — Real-World Entities
Think of a Cooling System as an ADT.

Instead of writing raw variables like current_temp, fan_speed…

We create a conceptual entity with clean operations:

• TurnOnCoolingSystem()
• SetTargetTemperature()
• GetCurrentTemperature()

The rest of the program interacts with these concepts, not the raw
hardware data.
Treating Classes as ADTs
Classes Are ADTs Plus More The Guideline How to Apply It
A class is essentially an Abstract Data Treat every class you write as an ADT. Identify the data, define the operations,
Type with a couple of extra concepts and restrict access to the raw data.
added (like inheritance and
polymorphism).
6.2 Good Class Interfaces
The interface of a class defines how other parts of the program interact with it.

The Iceberg Analogy

A good class interface is like the tip of an iceberg. Only a small, essential part is
exposed above the surface. The massive, complex implementation details
remain hidden underwater.
Good Abstraction in Interfaces

Consistent Level of Abstraction Domain-Relevant Design Conceptual Belonging


A class interface should present a It should make sense in the domain of the Every routine in the interface must clearly
consistent level of abstraction. problem, not just the computer's memory. belong to the concept the class
represents.
C++ EXAMPLE

Example of a Good Class Interface


class Employee {
public:
// Constructors
Employee( FullName name, String address, String workPhone );

// Public routines (Consistent Abstraction)


FullName GetName() const;
String GetAddress() const;
String GetWorkPhone() const;
JobClassification GetJobClassification() const;

private:
// Hidden implementation
};

Observation: Every public function clearly relates to the concept of an "Employee".


The Danger: Erosion Under
Modification
As a class is modified and extended over time, its original, clean abstraction can
erode.

Programmers sometimes add utility functions to a class simply because "the data is
already there," rather than because the function conceptually belongs to the class.
C++ EXAMPLE

Example: An Eroding Interface


class Employee {
public:
FullName GetName() const; // Good

// Eroding Abstraction: Why are these here?!


bool IsZipCodeValid( Address address );
bool IsPhoneNumberValid( PhoneNumber phoneNumber );
SqlQuery GetQueryToCreateNewEmployee() const;
};

Critique: Validating a zip code or exposing SQL query details breaks the "Employee" abstraction. These belong in a Validator class or
Database class.
Encapsulation vs. Abstraction
Abstraction Encapsulation
Helps you manage complexity by providing models that allow you to The enforcer. It prevents you from looking at the implementation
ignore implementation details. details even if you want to.

"You are allowed to look at it at a high level." "You are not allowed to look at it at a low level."
Guidelines for Good Encapsulation
Minimize Accessibility Do Not Expose Member Data Don't Make Assumptions
About the Users
Make everything private by default. Only Avoid making variables public. Use
make members public if absolutely getter/setter methods if external access A class shouldn't depend on other
necessary. is needed. classes calling its methods in a specific,
undocumented order.
Exposing Implementation Details (C++ Issue)
In C++, the language structure requires declaring private members in the header The C++ Header Problem
file (.h).
Private members are visible in the .h file but not accessible in
This means programmers reading the header can see the private code — a subtle but important distinction.
implementation details, even though they can't access them in code.

Solution

Discipline. Treat the private section as off-limits when reading the class
interface.
Read-Time Convenience Rule

"Read-time convenience" is not a reliable indicator of good encapsulation.

The Temptation The Rule


Just because it's easier to make a variable public so another class Always prioritize the integrity of the abstraction over writing slightly
can read it quickly, doesn't mean you should. less code.
6.3 Design and Implementation Issues
The two primary ways to relate one class to another are Containment and Inheritance.

Containment Inheritance
One class contains an instance of another class ("has a" A derived class inherits data and behavior from a base class ("is a"
relationship). relationship).

Choosing the wrong relationship is a major source of software complexity and bugs.
Containment ("Has a" Relationship)
Containment (or Composition) is when one class contains an instance
of another class.

Examples

• An Employee has a Name.


• A Car has an Engine.

Containment is the preferred method of combining classes because it


maintains loose coupling.
The "7 ± 2" Data Member Rule

7±2 7
Data Members Max Working Memory Limit
Be critical of classes that contain more than about seven (7 ± 2) data Research shows human working memory can only track about 7 discrete
members. items at once.

If a class has 15 member variables, it's likely doing too much. Consider breaking it down into smaller, contained classes.
Inheritance ("Is a" Relationship)

What It Is Rule of Thumb Examples


Inheritance allows a derived class to inherit Use inheritance ONLY when a derived • A Manager is an Employee.
data and behavior from a base class. class "is a" more specific version of the • A Circle is a Shape.
base class.
The Liskov Substitution Principle (LSP)

LSP Definition: A program that uses a base class should be able to substitute a derived class without knowing it, and without breaking the
program.

If you have a routine that accepts an Employee, it should perfectly accept a Manager without crashing or requiring special if statements.
Types of Inherited Routines
When designing a base class, you must decide how derived classes handle its routines:

Abstract Overridable Overridable Non-Overridable


Inherits the interface only (must be Inherits the interface and a default Inherits interface and implementation
implemented by derived class). implementation (can be overridden). (cannot be changed).
Inherit Only What You Want
Be Intentional Don't Misuse Inheritance for Scott Meyers' Rule
Reuse
Be intentional about what you inherit. "Public inheritance means 'is a'. Commit
Don't use inheritance just because you this rule to memory." — Scott Meyers.
want to reuse one utility function from
another class. Use containment instead.
Avoid Deep Inheritance Trees
Deep inheritance trees (e.g., more than 2 or 3 levels) significantly increase Why It Matters
complexity.
Each additional level of inheritance multiplies the cognitive
It becomes impossible to understand a class without reading the code of 5 load required to understand any single class in the hierarchy.
different superclasses scattered across multiple files.

Guideline

Keep inheritance hierarchies shallow.


Be Suspicious of Single-Derived Classes

The Warning Sign The Likely Cause The Guideline


If a base class has only one derived class, This usually indicates a programmer is Make current work clear and simple. Don't
be suspicious. "designing ahead" (anticipating future add inheritance structures until they are
needs that don't exist yet). actually needed.
Overriding to Do Nothing (Anti-
Pattern)
Red Flag
A derived class overrides a base class method but leaves the body empty.

Example
Base class Cat has Scratch(). Derived class DeclawedCat overrides
Scratch() to do nothing.

The Problem
This violates the interface contract (LSP). A user of the Cat class expects a
scratch, but gets nothing.
Multiple Inheritance
Inheriting from more than one base class at the same time.

When It's Useful The Problem Recommendation


Useful in specific patterns (like mixins or Introduces massive complexity — the Avoid multiple inheritance of
interface inheritance). "Diamond Problem". implementations. Inheriting multiple
interfaces is generally safer.
Containment vs. Inheritance Summary
Shared Data, No Behavior Shared Data and Behavior Golden Rule
If classes share common data but not If classes share common behavior and Inherit when you want the base class to
behavior → Create a common object data → Inherit from a common base control your interface; contain when you
they both contain. class. want to control your own interface.
The Law of Demeter

Minimize the extent to which a class collaborates with other classes.

The Rule Violation Example

A class should only talk to its immediate friends (contained objects, [Link]().GetManager().GetName()
objects passed as parameters).
This tightly couples the code to the internal structure of multiple
classes.
Class Constructors
01 02 03

Initialize All Member Data Deferred Initialization Singleton Pattern


Initialize all member data in the constructor. If a class cannot be fully initialized in the Enforce the "Singleton" property using private
constructor (e.g., waiting for file read), use a constructors if only one instance should exist.
private constructor and provide a public
initialization method (Factory Method).
Deep vs. Shallow Copies
Shallow Copy Deep Copy
Copies memory addresses (pointers). Two objects point to the same Creates a completely new copy of the underlying data.
data.

Be explicit about which copy strategy your class uses, as unexpected shallow copies lead to severe memory corruption bugs.
6.4 Reasons to Create a Class
Why should we group code into a class?

1. Model Real-World Objects 2. Model Abstract Objects


E.g., Employee, Timecard, Bill. E.g., a Shape class that abstracts Circle and Square.
Reasons to Create a Class (Complexity)
1 2 3

Reduce Complexity Isolate Complexity Hide Implementation Details


Hide complex algorithms behind a simple Put the messiest, most error-prone code in Don't let the system know you are using a
class interface. one isolated class so the rest of the database vs. a flat file.
system is safe.
Reasons to Create a Class (Modularity)
Limit Effects of Changes Hide Global Data Streamline Parameter Passing
If a business rule changes, you only Turn global variables into private Instead of passing 7 variables to a
update one class. members of a class with controlled function, pass 1 object.
access.
Reasons to Create a Class (Design)

9. Make Central Points of Control 10. Facilitate Reusable Code 11. Package Related Operations
One class manages the database Well-designed classes can be ported to E.g., a Math class that holds Sin(), Cos(),
connection pool. other projects. Tan().
Classes to Avoid: God Classes
What Are God Classes?

God Classes: Omniscient classes that know everything and do


everything.

How to Spot One

If a class spends its time retrieving data from other classes using
Get() routines and processing it (digging into their business), it is a
God Class.

Fix

Move the processing logic back into the classes that actually own the
data.
Classes to Avoid: Irrelevant & Verbs
Irrelevant Classes Verb Classes
A class with only data but no behavior (just a glorified struct). A class named after an action (e.g., DatabaseInitialization or
Consider demoting it to attributes of another class. StringBuilder) that has no data. This should usually be a routine
(function) inside another class, not a class itself.
6.5 Language-Specific Issues
Different languages handle object-oriented concepts differently.

Java C++ Visual Basic


All routines are overridable by default Routines are NOT overridable by default Must explicitly use overridable and
(unless marked final). (must be marked virtual). overrides keywords.

Always learn the specifics of your chosen language.


6.6 Beyond Classes: Packages
As systems grow, classes alone are not enough to manage complexity.

The next level of organization is the Package (or Namespace / Subsystem).

Packages group related classes together (e.g., all database access classes in one
package, all UI classes in another).
Checklist — Class Quality (Part 1)

Abstract Data Types Abstraction Interface


Have you thought of the class as an ADT? Does the class have a central, clear Does the interface make it obvious how to
purpose? Is it well-named? use the class as a black box?
Checklist — Class Quality (Part 2)

Encapsulation Inheritance Implementation


Are member data kept private? Does it strictly model an "is-a" relationship Does it contain fewer than 7±2 data
(LSP)? members? Are all members initialized in
the constructor?
Summary
Treat Classes as ADTs
Treat classes as Abstract Data Types (ADTs).

Interfaces Should Hide Something


A class interface should hide something (an implementation detail or design
decision).

Prefer Containment Over Inheritance


Prefer Containment ("has a") over Inheritance ("is a").

Your Best Tool for Complexity


Classes are your absolute best tool for managing software complexity. Take
the time to design them well.

You might also like