The class 'Person' is defined with two private fields: '_name' of type String and '_age' of type int. These fields are not accessible outside the class directly.
💡 Private fields prevent external code from directly accessing or modifying sensitive data, enforcing encapsulation.
💡 Data hiding is implemented by marking fields as private (underscore prefix in Python).
fill_cells
Add Public Getter for Name
A public method 'getName' is added to the 'Person' class to return the value of the private field '_name'.
💡 Getters provide controlled read access to private data without exposing the field directly.
Line:def getName(self):
return self._name
💡 Encapsulation allows safe access to private data via public methods.
fill_cells
Add Public Setter for Name
A public method 'setName' is added to allow controlled modification of the private field '_name'. It accepts a String parameter and assigns it to '_name'.
💡 Setters allow validation or control when changing private data, preventing invalid states.
Line:def setName(self, name):
self._name = name
💡 Encapsulation controls how data is modified, not just accessed.
fill_cells
Add Public Getter for Age
A public method 'getAge' is added to return the value of the private field '_age'.
💡 Getter methods provide read access to private fields, maintaining encapsulation.
Line:def getAge(self):
return self._age
💡 Consistent use of getters for all private fields enforces encapsulation.
fill_cells
Add Public Setter for Age
A public method 'setAge' is added to allow controlled modification of the private field '_age'. It accepts an int parameter and assigns it to '_age'.
💡 Setters enable validation or control logic when modifying private data.
Line:def setAge(self, age):
self._age = age
💡 Setters complete the encapsulation mechanism by controlling data modification.
setup
Create Instance of Person
An instance 'person' of the 'Person' class is created. Initially, private fields '_name' and '_age' are None or default values.
💡 Creating an object is necessary to use the encapsulated data and methods.
Line:person = Person()
💡 Encapsulation applies to each object instance, protecting its own data.
insert
Set Name Using Setter
The 'setName' method is called on the 'person' instance with the argument 'Alice'. This updates the private field '_name' to 'Alice'.
💡 Using setters to modify private fields ensures controlled updates.
Line:person.setName('Alice')
💡 Direct field modification is avoided; setter method controls the update.
insert
Set Age Using Setter
The 'setAge' method is called on the 'person' instance with the argument 30. This updates the private field '_age' to 30.
💡 Setters ensure that private data is updated only through controlled methods.
Line:person.setAge(30)
💡 Encapsulation protects data integrity by restricting direct access.
traverse
Get Name Using Getter
The 'getName' method is called on the 'person' instance to retrieve the value of the private field '_name'. It returns 'Alice'.
💡 Getters provide safe read access to private data without exposing the field.
Line:name = person.getName()
💡 Encapsulation allows reading private data safely through public methods.
traverse
Get Age Using Getter
The 'getAge' method is called on the 'person' instance to retrieve the value of the private field '_age'. It returns 30.
💡 Getters allow safe retrieval of private data, maintaining encapsulation.
Line:age = person.getAge()
💡 Encapsulation ensures data is accessed only through controlled interfaces.
reconstruct
Summary: Encapsulation Complete
The 'Person' class encapsulates its data by hiding private fields and exposing public getters and setters. The instance 'person' uses these methods to safely access and modify data.
💡 This final step shows the full encapsulation mechanism working together to protect and control data.
Line:# All previous code combined
💡 Encapsulation bundles data and methods, enforcing data hiding and controlled access.
Encapsulation - Data Hiding, Getters/Setters & Access Modifiers - Watch the Algorithm Execute, Step by Step
Watching this step-by-step helps you understand how encapsulation protects data and how getters/setters provide controlled access, which is hard to grasp by just reading code.
Step 1/11
·Active fill★Answer cell
Private fields enforce data hiding.
Person
−_name: String
−_age: int
Getter method added for controlled access.
Person
−_name: String
−_age: int
+getName()
Setter method added for controlled modification.
Person
−_name: String
−_age: int
+getName()
+setName()
Getter method added for '_age'.
Person
−_name: String
−_age: int
+getName()
+setName()
+getAge()
Setter method added for '_age'.
Person
−_name: String
−_age: int
+getName()
+setName()
+getAge()
+1 more
Instance created to demonstrate encapsulation in action.
Person
−_name: String
−_age: int
+getName()
+setName()
+getAge()
+1 more
Setter method used to update private field.
Person
−_name: String
−_age: int
+getName()
+setName()
+getAge()
+1 more
Setter method used to update private field.
Person
−_name: String
−_age: int
+getName()
+setName()
+getAge()
+1 more
Getter method used to access private field.
Person
−_name: String
−_age: int
+getName()
+setName()
+getAge()
+1 more
Getter method used to access private field.
Person
−_name: String
−_age: int
+getName()
+setName()
+getAge()
+1 more
Encapsulation pattern demonstrated fully.
Person
−_name: String
−_age: int
+getName()
+setName()
+getAge()
+1 more
Key Takeaways
✓ Encapsulation hides internal data by making fields private and exposes controlled access via public getters and setters.
Reading code alone may not reveal how data hiding protects integrity; watching the step-by-step shows how access is controlled.
✓ Getters and setters provide a safe interface to read and modify private data, allowing validation or logic to be added if needed.
Seeing the methods added and used clarifies their role beyond just simple data access.
✓ Creating an instance and using getters/setters demonstrates encapsulation in action, showing how objects protect their own state.
Understanding encapsulation requires seeing both the class design and how instances interact with it.
Practice
(1/5)
1. Imagine a class responsible for both data persistence and data validation. When a change in validation rules occurs, trace the impact on the class and explain what happens step-by-step.
easy
A. Only the validation methods need modification; persistence remains unaffected, so SRP is maintained.
B. Changing validation rules forces modifying the entire class, risking unintended side effects on persistence logic.
C. Validation changes automatically propagate to persistence without code changes due to tight coupling.
D. Persistence logic will break because validation and persistence are tightly integrated and inseparable.
Solution
Step 1: Identify responsibilities
The class handles both validation and persistence, two distinct reasons to change.
Step 2: Trace change impact
Changing validation rules requires modifying validation code inside the class.
Step 3: Side effects
Because persistence logic shares the class, changes risk affecting persistence unintentionally, increasing maintenance risk.
Step 4: SRP violation
This coupling violates SRP, as one reason to change (validation) affects unrelated functionality (persistence).
Final Answer:
Option B -> Option B
Quick Check:
One reason to change should not force changes in unrelated code -> SRP violation.
Hint: One reason to change means one place to modify.
Common Mistakes:
Assuming changes affect only related methods without side effects.
Believing tight coupling is harmless if code is in one class.
Thinking validation and persistence are always linked.
2. You have a payment processing system that currently uses multiple if-else statements to handle different payment methods like credit card, UPI, and net banking. The system needs to be extended frequently with new payment methods without modifying existing code. Which design approach best addresses this requirement?
easy
A. Use a brute force approach with nested if-else statements for each payment method.
B. Implement a strategy pattern where each payment method is encapsulated in its own class implementing a common interface.
C. Use a recursive function that selects payment methods based on input parameters.
D. Apply a greedy algorithm to select the payment method with the lowest processing fee.
Solution
Step 1: Understand the problem of frequent extension
The system requires adding new payment methods without modifying existing code, which violates the open-closed principle if using if-else chains.
Step 2: Identify the design pattern that encapsulates behaviors
The strategy pattern encapsulates each payment method in its own class implementing a common interface, allowing easy extension by adding new classes without changing existing code.
Final Answer:
Option B -> Option B
Quick Check:
Strategy pattern replaces conditionals with polymorphism [OK]
Hint: Replacing conditionals with polymorphism enables easy extension [OK]
Common Mistakes:
Thinking recursion or greedy algorithms solve extensibility here
3. Identify the bug in the following singleton implementation that aims to use lazy initialization with double-checked locking in Java-like pseudocode:
class Singleton {
private static Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
What is the subtle bug that can cause thread-safety issues?
medium
A. The synchronized block is too large, causing unnecessary locking overhead.
B. The instance variable is not declared volatile, risking instruction reordering.
C. The first null check outside synchronized block is redundant and should be removed.
D. The constructor is not private, allowing multiple instances externally.
Hint: Volatile prevents instruction reordering in double-checked locking [OK]
Common Mistakes:
Ignoring volatile keyword necessity
Thinking synchronized block size is the bug
Assuming constructor privacy is the main issue here
4. If the parking lot system needs to support dynamic pricing based on spot location, time, and vehicle type, which design approach best supports this extensibility without modifying existing classes?
hard
A. Use the Strategy pattern to encapsulate different pricing algorithms and assign them dynamically
B. Add pricing logic directly inside the ParkingSpot class with multiple if-else conditions
C. Hardcode pricing rules in the ParkingLot class for all spot and vehicle combinations
D. Create separate subclasses of ParkingSpot for each pricing rule variant
Solution
Step 1: Identify extensibility needs
Pricing rules vary by multiple factors and may change frequently.
Step 2: Evaluate design options
Embedding logic in ParkingSpot or ParkingLot leads to rigid, hard-to-maintain code. Subclass explosion is unmanageable.
Step 3: Recognize Strategy pattern benefits
Strategy encapsulates pricing algorithms, allowing dynamic assignment and easy extension without modifying existing classes.
Hint: Use Strategy pattern to keep pricing logic flexible and decoupled [OK]
Common Mistakes:
Embedding complex logic inside core classes
Using inheritance for every pricing variant
5. Suppose you want to extend the observer pattern to allow observers to receive multiple notifications for the same event type without unsubscribing (i.e., observers can be registered multiple times for the same event). Which modification to the thread-safe observer pattern implementation below correctly supports this requirement without breaking thread safety or notification correctness?
Options:
A) Change the observers data structure from a set to a list per event type and allow duplicates.
B) Keep using a set but add a counter for each observer to track multiple subscriptions.
C) Use a dictionary mapping observers to their subscription counts per event type.
D) Use a queue per event type to enqueue notifications and process them asynchronously.
hard
A. Keep using a set but add a counter for each observer to track multiple subscriptions.
B. Change the observers data structure from a set to a list per event type and allow duplicates.
C. Use a dictionary mapping observers to their subscription counts per event type.
D. Use a queue per event type to enqueue notifications and process them asynchronously.
Solution
Step 1: Understand the requirement
Observers can subscribe multiple times to the same event type and should receive multiple notifications accordingly.
Step 2: Evaluate data structure changes
Using a set alone disallows duplicates. A list allows duplicates but is not thread-safe and inefficient for removals. A dictionary mapping observers to counts tracks multiple subscriptions safely.
Step 3: Choose the best approach
Adding a counter per observer in the set (Keep using a set but add a counter for each observer to track multiple subscriptions.) or using a dictionary (Use a dictionary mapping observers to their subscription counts per event type.) can work, but adding a counter per observer in the set is simpler and keeps thread safety with minimal changes.
Final Answer:
Option A -> Option A
Quick Check:
Counting subscriptions per observer preserves multiple notifications safely [OK]
Hint: Track subscription counts to allow multiple notifications [OK]
Common Mistakes:
Using list causes concurrency issues and inefficient removals
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.