Programming in Visual Basic

Chapter 4
Object-Oriented Programming in Visual Basic

Post Graduate Program in Computer Science Engineering
Programming in Visual Basic   •   Post Graduate

4. Object-Oriented Programming in Visual Basic

Introduction and Context

Object-oriented programming (OOP) is a method of designing software around objects that combine state and behavior. Visual Basic, particularly Visual Basic .NET, supports OOP through classes, structures, interfaces, inheritance, polymorphism, properties, methods, events, and generics. These features allow programmers to represent business entities and system responsibilities in a form that is easier to understand, test, extend, and maintain than a purely procedural design.

In a procedural program, attention is primarily directed toward sequences of instructions and the data passed between procedures. In an object-oriented program, the central question is different: which objects exist in the problem domain, what responsibilities do they have, and how should they collaborate? A banking application may contain `Account`, `Customer`, and `Transaction` objects; a university system may contain `Student`, `Course`, and `Enrollment` objects. The quality of the solution depends not only on correct syntax but also on sound allocation of responsibilities.

Visual Basic's object model is based on the .NET Common Language Runtime. Consequently, classes written in Visual Basic can use framework libraries and interoperate with components written in other .NET languages. This chapter develops the principal OOP mechanisms in Visual Basic and relates them to design quality, domain modeling, and professional software development.

Learning Objectives

After studying this chapter, the reader should be able to:

1. Explain classes, objects, members, constructors, and object lifetime in Visual Basic.

2. Apply encapsulation, access control, inheritance, polymorphism, abstraction, and interfaces.

3. Distinguish classes from structures and shared members from instance members.

4. Use generics and composition to build reusable designs.

5. Apply SOLID principles when evaluating and refactoring object-oriented code.

6. Model a domain using appropriate relationships and responsibilities.

7. Identify design risks, ethical concerns, and maintainability problems in OOP systems.

The Object Model: From Domain Concepts to Code

A class is a blueprint that defines the data and operations associated with a type. An object is a runtime instance of that class. Fields store implementation data, while properties provide controlled access to data. Methods define operations, and events notify other objects that something has occurred.

```vb

Public Class BankAccount

Private _balance As Decimal

Public Sub New(accountNumber As String)

Me.AccountNumber = accountNumber

End Sub

Public ReadOnly Property AccountNumber As String

Public ReadOnly Property Balance As Decimal

Get

Return _balance

End Get

End Property

Public Sub Deposit(amount As Decimal)

If amount <= 0D Then Throw New ArgumentOutOfRangeException(NameOf(amount))

_balance += amount

End Sub

End Class

```

The `Private` field cannot be changed directly by external code. The constructor establishes the account number, and `Deposit` enforces a business rule. This is more robust than exposing `Balance` as a public variable because the class controls how its state changes.

An object is created with `New`:

```vb

Dim account As New BankAccount("A-101")

account.Deposit(500D)

Console.WriteLine(account.Balance)

```

`Me` refers to the current object. `Nothing` represents the absence of an object reference. Variables of a class type hold references, so two variables may refer to the same object:

```vb

Dim first = account

first.Deposit(100D)

' account.Balance is now 600

```

The .NET garbage collector eventually reclaims objects that are no longer reachable. Developers should nevertheless release external resources such as files, database connections, and network handles using `IDisposable` and `Using` blocks. Memory management does not eliminate the need for resource management.

Constructors and Object Invariants

A constructor is a `Sub New` procedure that runs when an object is created. Constructors may receive parameters, call another constructor through `Me.New`, or invoke a base-class constructor through `MyBase.New`. Constructor overloading supports different valid creation paths, but excessive overloads can make an API confusing.

A constructor should establish the object's invariant: a condition that must remain true whenever the object is observable. For example, an employee's identifier should not be empty, and a percentage should remain between zero and one hundred. Validation should occur at the boundary where invalid data enters the object.

```vb

Public Class Employee

Public Sub New(id As Integer, name As String)

If id <= 0 Then Throw New ArgumentOutOfRangeException(NameOf(id))

If String.IsNullOrWhiteSpace(name) Then Throw New ArgumentException("Name is required")

Me.Id = id

Me.Name = name.Trim()

End Sub

Public ReadOnly Property Id As Integer

Public Property Name As String

End Class

```

A class may define a `Finalize` method, but finalization is nondeterministic and should not be used for ordinary resource cleanup. Prefer `IDisposable` for deterministic cleanup.

Encapsulation and Access Control

Encapsulation means combining data and operations while restricting inappropriate access to implementation details. It is not merely the use of private fields; it is the protection of valid state and the publication of a purposeful interface.

Visual Basic provides several access modifiers. `Public` permits access from any referencing code. `Private` limits access to the declaring type. `Protected` allows the declaring type and derived types to access a member. `Friend` permits access within the same assembly, while `Protected Friend` combines assembly and inheritance access. `Private Protected` limits access to derived types within the same assembly.

A well-designed class exposes behavior rather than indiscriminate data. A property can be read-only, write-only, or independently controlled:

```vb

Public ReadOnly Property Total As Decimal

Public Property Status As OrderStatus

```

An auto-implemented property is convenient, but a calculated or validated property may require a backing field. Encapsulation also includes avoiding unnecessary setters, exposing collections as read-only views, and preventing callers from placing an object in an invalid state.

Shared Members and Type-Level Behavior

A `Shared` member belongs to the class itself rather than to a particular instance. It can be accessed through the type name and cannot directly use instance members. Shared members are appropriate for stateless utility operations, constants, factories, or values common to all instances.

```vb

Public Class IdentifierGenerator

Private Shared _nextValue As Integer = 1000

Public Shared Function NextId() As Integer

_nextValue += 1

Return _nextValue

End Function

End Class

```

Shared mutable state can create hidden dependencies and concurrency problems. It should therefore be used carefully. A singleton or global service may be difficult to test because it couples unrelated code to one shared instance. Dependency injection is often preferable when a service has behavior or external dependencies.

Inheritance and Polymorphism

Inheritance expresses an “is-a” relationship. A derived class inherits accessible members from a base class and can add or specialize behavior. Visual Basic uses `Inherits`:

```vb

Public Class SavingsAccount

Inherits BankAccount

Public Sub New(number As String, rate As Decimal)

MyBase.New(number)

InterestRate = rate

End Sub

Public Property InterestRate As Decimal

End Class

```

A derived class should be substitutable for its base class. If a subclass changes the meaning of inherited operations or requires callers to test its concrete type constantly, inheritance is probably inappropriate.

Polymorphism allows a base-class or interface reference to refer to different concrete objects. Overriding is runtime polymorphism:

```vb

Public MustInherit Class Notification

Public MustOverride Sub Send(message As String)

End Class

Public Class EmailNotification

Inherits Notification

Public Overrides Sub Send(message As String)

Console.WriteLine("Email: " & message)

End Sub

End Class

```

`Overloads` provides compile-time method selection based on different signatures. `Overrides` replaces an overridable inherited implementation. `NotOverridable` prevents further overriding, and `Shadows` hides an inherited member; shadowing should be used cautiously because it can produce different behavior depending on the variable's declared type.

Abstract and Sealed Classes

A `MustInherit` class is abstract: it cannot be instantiated and is intended to provide common behavior or a conceptual contract. A `MustOverride` member has no implementation in the abstract class and must be implemented by a concrete derived class. Abstract classes are useful when related types share state or implementation.

A `NotInheritable` class is sealed. It prevents inheritance and is suitable when extension would violate invariants, create security concerns, or have no meaningful interpretation. A sealed class can still implement interfaces and be composed into other objects. Abstract and sealed express opposite extension policies: one requires specialization, while the other prohibits it.

Interfaces as Contracts

An interface describes capabilities without prescribing implementation. Visual Basic uses `Interface` and `Implements`:

```vb

Public Interface IPaymentProcessor

Function Process(amount As Decimal) As Boolean

End Interface

Public Class CardProcessor

Implements IPaymentProcessor

Public Function Process(amount As Decimal) As Boolean _

Implements IPaymentProcessor.Process

Return amount > 0D

End Function

End Class

```

Interfaces support substitutability, loose coupling, multiple capability contracts, and testing with substitutes. A class may implement several interfaces, whereas Visual Basic class inheritance has one direct base class. An interface should be cohesive and client-focused; a large interface forces clients to depend on operations they do not need.

Abstract classes and interfaces overlap but are not identical. An abstract class can provide protected state and shared implementation, while an interface primarily defines a contract. The choice depends on whether the relationship represents shared identity and implementation or merely a capability.

Structures, Value Semantics, and Reference Semantics

A `Structure` is a value type. Assignment copies its value, whereas assignment of a class variable copies an object reference. Structures are appropriate for small, logically indivisible values such as coordinates, dates, or measurements.

```vb

Public Structure Coordinate

Public Sub New(latitude As Double, longitude As Double)

Me.Latitude = latitude

Me.Longitude = longitude

End Sub

Public ReadOnly Property Latitude As Double

Public ReadOnly Property Longitude As Double

End Structure

```

Large mutable structures can cause copying overhead and confusing behavior. A structure should generally be small, represent a value rather than an identity, and avoid mutable shared state. Boxing occurs when a value type is converted to `Object` or an interface reference; unboxing requires conversion back to the correct value type.

Composition and Aggregation

Composition models a “has-a” relationship. An `Order` may contain order lines, and a `ReportService` may use a formatter. Composition is often safer than inheritance because the containing object can delegate to replaceable collaborators without acquiring an unwanted public hierarchy.

```vb

Public Class ReportService

Private ReadOnly _formatter As IReportFormatter

Public Sub New(formatter As IReportFormatter)

_formatter = formatter

End Sub

Public Function Create(data As Object) As String

Return _formatter.Format(data)

End Function

End Class

```

Dependency injection supplies dependencies from outside, as in the constructor above. It improves testability and makes dependencies visible. Aggregation is a weaker whole-part relationship in which the part may exist independently; composition normally implies stronger ownership and coordinated lifetime.

Generics and Reusable Abstractions

Generics allow types and methods to operate safely over a range of data types without repeated implementations or unnecessary conversions. `List(Of T)`, `Dictionary(Of K, V)`, and `Nullable(Of T)` are common examples.

```vb

Public Function FindById(Of T As {Class, IEntity})(

items As IEnumerable(Of T), id As Integer) As T

Return items.FirstOrDefault(Function(x) x.Id = id)

End Function

```

The constraint specifies what operations are valid for `T`. Common constraints include `Class`, `Structure`, `New`, a base class, or an interface. Generics provide compile-time type checking and express reusable algorithms clearly. They should not be introduced merely to make code appear sophisticated; the abstraction must represent a genuine family of related types.

SOLID as a Design Review Framework

SOLID is a group of principles for managing dependencies and change.

The Single Responsibility Principle states that a class should have one reason to change. An invoice class that calculates totals, writes files, sends email, and persists database records has several responsibilities.

The Open–Closed Principle recommends that software be open for extension but closed for inappropriate modification. Polymorphic payment processors can allow a new payment method without changing a stable checkout algorithm.

The Liskov Substitution Principle requires derived types to honor the expectations established by their base abstractions. A subtype should not reject valid base-class operations or weaken guarantees.

The Interface Segregation Principle favors several focused interfaces over one large interface. A printer that only scans should not be forced to implement fax operations.

The Dependency Inversion Principle states that high-level policy should depend on abstractions, not directly on low-level details. A service should depend on `IPaymentProcessor`, not on a particular card gateway.

SOLID principles are heuristics, not rigid laws. Applying them mechanically can produce excessive interfaces, indirection, and fragmented code. The appropriate design balances flexibility with comprehensibility.

Domain Modeling and Practical Application

Domain modeling begins by identifying important concepts, their attributes, invariants, relationships, and responsibilities. Nouns in requirements may suggest entities, but verbs often reveal services and interactions. A domain model should distinguish entities, which possess identity over time, from value objects, which are defined by their values. It should also distinguish domain rules from infrastructure concerns such as SQL, file formats, and user-interface controls.

For an online order system, `Customer` and `Order` may be entities; `Money` and `Address` may be value objects. An order can contain order lines through composition, while payment processing can be represented by an interface. A repository abstraction can separate domain logic from storage technology.

A professional implementation should also consider thread safety, logging, validation, exception policy, serialization, versioning, and testability. OOP is not an excuse to create a class for every noun. The model should clarify business decisions and reduce change costs.

Illustrative Case Study: Refactoring a University Enrollment System

Background

A university has an enrollment application that registers students for courses. The initial system contains a large `EnrollmentManager` class. It validates prerequisites, calculates fees, writes database records, sends email, and generates reports.

Situation and Analysis

The class is difficult to test because every operation requires a live database and email server. Adding a new fee policy requires changing the manager, and different course types use conditional statements throughout the code. The design violates single responsibility and dependency inversion. It also exposes mutable collections, allowing callers to alter enrollment data without validation.

Approach

The domain is divided into `Student`, `Course`, `Enrollment`, and `Fee` concepts. `Enrollment` protects its status through methods such as `Confirm` and `Withdraw`. Course-specific rules implement `IEnrollmentPolicy`, while fee calculation uses `IFeeCalculator`. A `StudentRepository` interface hides persistence details. The application service receives these abstractions through its constructor.

```vb

Public Interface IEnrollmentPolicy

Function CanEnroll(student As Student, course As Course) As Boolean

End Interface

Public Class EnrollmentService

Private ReadOnly _policy As IEnrollmentPolicy

Private ReadOnly _repository As IEnrollmentRepository

Public Sub New(policy As IEnrollmentPolicy,

repository As IEnrollmentRepository)

_policy = policy

_repository = repository

End Sub

Public Sub Enroll(student As Student, course As Course)

If Not _policy.CanEnroll(student, course) Then

Throw New InvalidOperationException("Enrollment is not permitted")

End If

_repository.Save(New Enrollment(student, course))

End Sub

End Class

```

A test can now provide a fake policy and in-memory repository. The user interface is not responsible for enforcing domain rules, and a database implementation can be replaced without changing enrollment logic.

Outcome and Learning Points

The refactored system has clearer responsibilities, more isolated tests, and a simpler path for adding policy variants. The case illustrates that abstraction is valuable when it represents a real point of variation. It also demonstrates that composition and interfaces can express collaboration without constructing a deep inheritance hierarchy.

Challenges, Risks, and Common Mistakes

A frequent mistake is exposing public fields or unrestricted setters, which permits invalid state. Another is using inheritance only to reuse a few lines of code; composition is usually more adaptable. Deep hierarchies make behavior difficult to trace, while excessive abstraction creates needless indirection.

Misusing `Shared` mutable members can cause test interference and race conditions. Confusing `Overloads`, `Overrides`, and `Shadows` can produce unexpected dispatch behavior. Mutable structures may silently modify copies rather than the intended value. Constructors that perform network or database operations make object creation fragile and difficult to test.

Developers should avoid catching exceptions without meaningful action, returning `Nothing` where a clear failure is required, and allowing domain rules to exist only in the interface layer. Names should express roles, methods should have focused effects, and public APIs should be kept deliberately small.

Professional, Ethical, and Governance Considerations

Object-oriented design affects accountability and data protection. Encapsulation can reduce unauthorized modification, but `Private` is not a security boundary against a determined administrator or runtime inspection. Sensitive information should be minimized, encrypted where appropriate, and excluded from logs. Domain objects handling identity, payment, or health data should enforce authorization and audit requirements through appropriate application architecture.

Developers must also consider accessibility, localization, records retention, and regulatory obligations. A class that silently discards invalid transactions may create financial or legal risk. Clear validation, traceable decisions, secure dependency configuration, and meaningful tests support professional responsibility.

Current Developments and Best Practices

Modern Visual Basic applications commonly use dependency injection, automated unit testing, asynchronous APIs, nullable reference analysis, immutable value objects, and layered or hexagonal architectures. Generics and interfaces remain central to reusable libraries, while analyzers and integrated development environments identify accessibility, naming, and design problems early.

Useful practices include:

  • Keep invariants inside the object that owns them.
  • Prefer composition and small interfaces over unnecessary inheritance.
  • Program to abstractions at genuine variation points.
  • Make dependencies explicit through constructors.
  • Use read-only views for collections exposed by a class.
  • Separate domain policy from persistence and user-interface code.
  • Test behavior and contracts rather than private implementation details.
  • Review abstractions periodically; remove those that do not reduce change or complexity.

Chapter Summary

Object-oriented programming in Visual Basic organizes software around objects that protect state and provide behavior. Classes define reference types, while structures define value-oriented types. Constructors establish invariants; access modifiers and properties support encapsulation. Inheritance and overriding enable runtime polymorphism, while abstract and sealed classes control extension. Interfaces express capabilities and support loose coupling. Shared members provide type-level behavior but require caution when state is mutable. Generics provide reusable, type-safe abstractions, and composition often offers a more flexible alternative to inheritance. SOLID principles help evaluate responsibilities, substitutability, interfaces, and dependencies. Effective domain modeling connects these mechanisms to real business concepts while considering testing, security, governance, and maintainability.

Key Terms

Class: A blueprint defining the members and behavior of a type.

Object: A runtime instance of a class.

Constructor: A `Sub New` procedure that initializes an object.

Encapsulation: Protection and controlled exposure of an object's state and behavior.

Property: A member that provides controlled access to a value.

Inheritance: Derivation of a new class from an existing class.

Polymorphism: Use of one abstraction to work with objects of different concrete types.

Overriding: Replacing an overridable inherited implementation.

Interface: A contract specifying operations or capabilities.

Abstract class: A `MustInherit` class that cannot be instantiated directly.

Sealed class: A `NotInheritable` class that cannot be subclassed.

Structure: A value type whose assignment copies its value.

Composition: A whole-part relationship implemented by containing collaborating objects.

Shared member: A class-level member associated with the type rather than an instance.

Generic: A type or method parameterized over one or more data types.

Invariant: A condition that must remain valid for an object.

Dependency injection: Supplying an object's collaborators from outside the object.

SOLID: Five principles for managing responsibility, extension, substitution, interfaces, and dependencies.

Domain model: A software representation of important concepts and rules in a problem domain.

Review Questions

Short Answer Questions

1. Distinguish a class from an object in Visual Basic.

2. Why should constructors establish object invariants?

3. What is the difference between `Overrides` and `Overloads`?

4. State two differences between a class and a structure.

5. Why can unrestricted public setters weaken encapsulation?

Long Answer Questions

1. Explain encapsulation in Visual Basic, including access modifiers, properties, validation, and collection exposure.

2. Compare abstract classes and interfaces, and recommend situations in which each is appropriate.

3. Explain inheritance, composition, and polymorphism using a suitable domain example.

4. Discuss all five SOLID principles and show how they can guide the design of a service class.

5. Describe how generics, dependency injection, and interfaces contribute to reusable and testable software.

Analytical / Application Questions

1. A payment class contains card validation, database persistence, email notification, and fee calculation. Analyze its design using SOLID principles and propose a revised structure.

2. A `Rectangle` subtype of `Shape` throws an exception when its inherited `Draw` operation is called. What design principle may be violated, and how would you redesign the abstraction?

3. Design a Visual Basic object model for a library system containing members, books, loans, and notifications. Identify entities, value objects, interfaces, composition relationships, and important invariants.

22. Multiple Choice Questions with Answers

Question 1

Which design best demonstrates encapsulation in a BankAccount class?

A. Making Balance a public field that any caller can change

B. Making Balance read-only and changing it only through validated Deposit and Withdraw methods

C. Using a Shared balance for every account

D. Allowing callers to replace the account's private fields

Correct Answer: B. Making Balance read-only and changing it only through validated Deposit and Withdraw methods

Question 2

What is the primary purpose of a constructor in an object-oriented class?

A. To establish the initial valid state of an object

B. To override every method inherited from a base class

C. To convert a class into a structure

D. To make all members Shared

Correct Answer: A. To establish the initial valid state of an object

Question 3

Which Visual Basic declaration prevents a class from being instantiated directly?

A. NotInheritable Class

B. MustInherit Class

C. Shared Class

D. Friend Class

Correct Answer: B. MustInherit Class

Question 4

A service depends on IPaymentProcessor rather than CardProcessor. Which SOLID principle is most directly illustrated?

A. Single Responsibility Principle

B. Liskov Substitution Principle

C. Dependency Inversion Principle

D. Interface Segregation Principle

Correct Answer: C. Dependency Inversion Principle

Question 5

What happens when a variable containing a class object is assigned to another variable?

A. The complete object is always copied

B. The second variable normally refers to the same object

C. The object is converted into a Structure

D. The original object is immediately finalized

Correct Answer: B. The second variable normally refers to the same object

Question 6

Which relationship is most appropriately represented by composition?

A. A ReportService using an injected IReportFormatter

B. A savings account inheriting from a bank account

C. A method being overloaded with another signature

D. A value being boxed as Object

Correct Answer: A. A ReportService using an injected IReportFormatter

Question 7

Why are small immutable value objects often suitable as Structures?

A. They must always support inheritance

B. They represent values and assignment copies their value

C. They automatically provide database persistence

D. They can contain only Shared members

Correct Answer: B. They represent values and assignment copies their value

Question 8

What is the main benefit of an interface in the enrollment case study?

A. It forces every class to inherit from one base class

B. It hides all validation from the system

C. It provides a replaceable contract for policies or repositories

D. It prevents polymorphism

Correct Answer: C. It provides a replaceable contract for policies or repositories

Question 9

Which situation most clearly violates the Single Responsibility Principle?

A. A class validates its own invariant

B. A formatter implements IReportFormatter

C. An enrollment manager validates rules, writes database records, sends email, and generates reports

D. A class has a private backing field for a property

Correct Answer: C. An enrollment manager validates rules, writes database records, sends email, and generates reports

Question 10

What does a generic constraint such as `Of T As {Class, IEntity}` accomplish?

A. It restricts T to types satisfying the specified requirements

B. It makes T a Shared member

C. It prevents compile-time type checking

D. It converts every T into a Structure

Correct Answer: A. It restricts T to types satisfying the specified requirements

Programming in Visual Basic
Chapter 4 of 14