Inheritance, Interfaces, and Polymorphism
Project: Polymorphic content system
You are viewing a free preview lesson.
Project Overview
এই practice project-এ আমরা একটি complete polymorphic course content system তৈরি করব।
Systemটি support করবে:
- Video, article এবং quiz content
- Shared abstract parent
- Content-specific behavior overriding
- Runtime polymorphism
- Polymorphic collections
- Optional download capability
- Quiz grading capability
- Interface-based notification dependency
- Constructor injection
- Composition-based completion rules
- Safe publication lifecycle
এই project-এ Module 3-এর core concepts একসঙ্গে ব্যবহার করা হবে:
Inheritance
super
Method overriding
Runtime polymorphism
Abstract classes
Interfaces
Polymorphic collections
Constructor injection
Composition
Delegation
Project Requirements
Content Rules
সব content item-এর থাকবে:
ID
Title
Published status
Estimated learning time
Content type
সব content item সরাসরি তৈরি করা যাবে না।
Valid concrete content types:
VideoLesson
ArticleLesson
QuizLesson
Video Lesson Rules
- Video URL required
- Duration positive হতে হবে
- শুধু secure
https://URL হলে publish করা যাবে - Downloadable হবে
- Estimated learning time হবে video duration
Article Lesson Rules
- Word count positive হতে হবে
- Download URL required
- কমপক্ষে 100 words না হলে publish করা যাবে না
- Download URL secure হতে হবে
- Estimated reading speed হবে 200 words per minute
- Partial minute round up হবে
Quiz Lesson Rules
- Question count positive হতে হবে
- Passing score
0থেকে100 - প্রতিটি question-এর estimated time 2 minutes
- Quiz grade করা যাবে
- Quiz downloadable নয়
Learner Progress Rules
- Learner name required
- Content required
- Completion policy required
- Progress
0থেকে100 - Progress backward নেওয়া যাবে না
- Completion rule composition দিয়ে determine হবে
Notification Rules
- Publication notification interface-এর মাধ্যমে পাঠানো হবে
- Business class concrete sender তৈরি করবে না
- Sender constructor দিয়ে inject করা হবে
- Fake sender দিয়ে behavior verify করা যাবে
Project Structure
src/main/java/io/liveklass/
├── Main.java
├── catalog/
│ └── ContentCatalog.java
├── content/
│ ├── ArticleLesson.java
│ ├── ContentItem.java
│ ├── Downloadable.java
│ ├── Gradable.java
│ ├── QuizLesson.java
│ └── VideoLesson.java
├── notification/
│ ├── ConsoleNotificationSender.java
│ ├── FakeNotificationSender.java
│ └── NotificationSender.java
├── progress/
│ ├── CompletionPolicy.java
│ ├── FullCompletionPolicy.java
│ ├── LearnerProgress.java
│ └── ThresholdCompletionPolicy.java
└── publishing/
└── ContentPublicationNotifier.java
Part 1: Define the Abstract Content Type
Path:
src/main/java/io/liveklass/content/ContentItem.java
package io.liveklass.content;
public abstract class ContentItem {
private final long id;
private final String title;
private boolean published;
protected ContentItem(
long id,
String title
) {
if (id <= 0) {
throw new IllegalArgumentException(
"Content ID must be positive."
);
}
if (
title == null
|| title.isBlank()
) {
throw new IllegalArgumentException(
"Content title is required."
);
}
this.id = id;
this.title = title.strip();
this.published = false;
}
public abstract int calculateEstimatedMinutes();
public abstract String getContentType();
protected abstract boolean isReadyForPublication();
public final boolean publish() {
if (published) {
return false;
}
if (!isReadyForPublication()) {
return false;
}
published = true;
return true;
}
public final long getId() {
return id;
}
public final String getTitle() {
return title;
}
public final boolean isPublished() {
return published;
}
@Override
public String toString() {
return "ContentItem{"
+ "id="
+ id
+ ", title='"
+ title
+ '\''
+ ", type="
+ getContentType()
+ ", estimatedMinutes="
+ calculateEstimatedMinutes()
+ ", published="
+ published
+ '}';
}
}
Design Review: ContentItem
Why Is It Abstract?
Generic content item independently meaningful নয়।
Application content types:
Video
Article
Quiz
এ ছাড়া direct generic object তৈরি করা prevent করা হয়েছে।
Why Are Some Methods Abstract?
calculateEstimatedMinutes()
getContentType()
isReadyForPublication()
এই behavior content type অনুযায়ী different।
প্রতিটি concrete child implementation দিতে বাধ্য।
Why Is publish() Final?
Publication flow সব content-এর জন্য consistent:
- Already published check
- Readiness check
- State update
Child শুধু readiness rule provide করবে।
পুরো lifecycle replace করতে পারবে না।
Why Are Fields Private?
Child classes raw state direct modify করতে পারবে না।
Parent invariants protected থাকে।
Part 2: Define Capability Interfaces
Downloadable.java
Path:
src/main/java/io/liveklass/content/Downloadable.java
package io.liveklass.content;
public interface Downloadable {
String getDownloadUrl();
default boolean hasDownloadAvailable() {
String url =
getDownloadUrl();
return url != null
&& !url.isBlank();
}
}
Gradable.java
Path:
src/main/java/io/liveklass/content/Gradable.java
package io.liveklass.content;
public interface Gradable {
int calculateScore(
int correctAnswers
);
boolean hasPassed(
int correctAnswers
);
}
Why These Are Interfaces
Downloadable এবং Gradable common parent identity নয়।
এগুলো capabilities।
VideoLesson is Downloadable
ArticleLesson is Downloadable
QuizLesson is Gradable
সব ContentItem downloadable বা gradable নয়।
তাই broad parent methods-এর পরিবর্তে focused interfaces ব্যবহার করা হয়েছে।
Part 3: Implement VideoLesson
Path:
src/main/java/io/liveklass/content/VideoLesson.java
package io.liveklass.content;
public final class VideoLesson
extends ContentItem
implements Downloadable {
private final String videoUrl;
private final int durationInMinutes;
public VideoLesson(
long id,
String title,
String videoUrl,
int durationInMinutes
) {
super(
id,
title
);
if (
videoUrl == null
|| videoUrl.isBlank()
) {
throw new IllegalArgumentException(
"Video URL is required."
);
}
if (durationInMinutes <= 0) {
throw new IllegalArgumentException(
"Video duration must be positive."
);
}
this.videoUrl =
videoUrl.strip();
this.durationInMinutes =
durationInMinutes;
}
@Override
public int calculateEstimatedMinutes() {
return durationInMinutes;
}
@Override
public String getContentType() {
return "VIDEO";
}
@Override
protected boolean isReadyForPublication() {
return videoUrl.startsWith(
"https://"
);
}
@Override
public String getDownloadUrl() {
return videoUrl;
}
public int getDurationInMinutes() {
return durationInMinutes;
}
}
VideoLesson Design Review
VideoLesson:
is a ContentItem
is Downloadable
It inherits:
ID
Title
Published status
Publication lifecycle
It owns:
Video URL
Video duration
Video-specific readiness
Part 4: Implement ArticleLesson
Path:
src/main/java/io/liveklass/content/ArticleLesson.java
package io.liveklass.content;
public final class ArticleLesson
extends ContentItem
implements Downloadable {
private static final int WORDS_PER_MINUTE =
200;
private final int wordCount;
private final String downloadUrl;
public ArticleLesson(
long id,
String title,
int wordCount,
String downloadUrl
) {
super(
id,
title
);
if (wordCount <= 0) {
throw new IllegalArgumentException(
"Word count must be positive."
);
}
if (
downloadUrl == null
|| downloadUrl.isBlank()
) {
throw new IllegalArgumentException(
"Article download URL is required."
);
}
this.wordCount =
wordCount;
this.downloadUrl =
downloadUrl.strip();
}
@Override
public int calculateEstimatedMinutes() {
return (
wordCount
+ WORDS_PER_MINUTE
- 1
) / WORDS_PER_MINUTE;
}
@Override
public String getContentType() {
return "ARTICLE";
}
@Override
protected boolean isReadyForPublication() {
return wordCount >= 100
&& downloadUrl.startsWith(
"https://"
);
}
@Override
public String getDownloadUrl() {
return downloadUrl;
}
public int getWordCount() {
return wordCount;
}
}
Why Reading Time Rounds Up
For:
201 words
Integer division directly করলে:
201 / 200 = 1
কিন্তু reading time প্রায় 2 minutes হওয়া উচিত।
Formula:
(
wordCount
+ WORDS_PER_MINUTE
- 1
) / WORDS_PER_MINUTE
Positive integers-এর division round up করে।
Examples:
200 words → 1 minute
201 words → 2 minutes
399 words → 2 minutes
400 words → 2 minutes
Part 5: Implement QuizLesson
Path:
src/main/java/io/liveklass/content/QuizLesson.java
package io.liveklass.content;
public final class QuizLesson
extends ContentItem
implements Gradable {
private static final int MINUTES_PER_QUESTION =
2;
private final int questionCount;
private final int passingScore;
public QuizLesson(
long id,
String title,
int questionCount,
int passingScore
) {
super(
id,
title
);
if (questionCount <= 0) {
throw new IllegalArgumentException(
"Question count must be positive."
);
}
if (
passingScore < 0
|| passingScore > 100
) {
throw new IllegalArgumentException(
"Passing score must be between 0 and 100."
);
}
this.questionCount =
questionCount;
this.passingScore =
passingScore;
}
@Override
public int calculateEstimatedMinutes() {
return questionCount
* MINUTES_PER_QUESTION;
}
@Override
public String getContentType() {
return "QUIZ";
}
@Override
protected boolean isReadyForPublication() {
return questionCount > 0;
}
@Override
public int calculateScore(
int correctAnswers
) {
validateCorrectAnswers(
correctAnswers
);
return correctAnswers
* 100
/ questionCount;
}
@Override
public boolean hasPassed(
int correctAnswers
) {
return calculateScore(
correctAnswers
) >= passingScore;
}
public int getQuestionCount() {
return questionCount;
}
public int getPassingScore() {
return passingScore;
}
private void validateCorrectAnswers(
int correctAnswers
) {
if (
correctAnswers < 0
|| correctAnswers > questionCount
) {
throw new IllegalArgumentException(
"Correct answer count is invalid."
);
}
}
}
QuizLesson Design Review
Quiz is:
A ContentItem
Gradable
Quiz is not automatically:
Downloadable
তাই getDownloadUrl() parent-এ রাখা হয়নি।
Unsupported method return বা exception avoid করা হয়েছে।
Part 6: Build a Polymorphic Catalog
Path:
src/main/java/io/liveklass/catalog/ContentCatalog.java
package io.liveklass.catalog;
import io.liveklass.content.ContentItem;
import io.liveklass.content.Downloadable;
import java.util.List;
public final class ContentCatalog {
private final List<ContentItem> contentItems;
public ContentCatalog(
List<ContentItem> contentItems
) {
if (contentItems == null) {
throw new IllegalArgumentException(
"Content items are required."
);
}
for (
ContentItem content
: contentItems
) {
if (content == null) {
throw new IllegalArgumentException(
"Content items cannot contain null."
);
}
}
this.contentItems =
List.copyOf(
contentItems
);
}
public int publishAll() {
int publishedCount = 0;
for (
ContentItem content
: contentItems
) {
if (content.publish()) {
publishedCount++;
}
}
return publishedCount;
}
public int calculateTotalMinutes() {
int totalMinutes = 0;
for (
ContentItem content
: contentItems
) {
totalMinutes +=
content
.calculateEstimatedMinutes();
}
return totalMinutes;
}
public void printSummaries() {
for (
ContentItem content
: contentItems
) {
System.out.println(
content.getContentType()
+ ": "
+ content.getTitle()
);
System.out.println(
"Estimated minutes: "
+ content
.calculateEstimatedMinutes()
);
System.out.println(
"Published: "
+ content.isPublished()
);
System.out.println();
}
}
public void printDownloads() {
for (
ContentItem content
: contentItems
) {
if (
content
instanceof Downloadable downloadable
&& downloadable
.hasDownloadAvailable()
) {
System.out.println(
content.getTitle()
);
System.out.println(
downloadable
.getDownloadUrl()
);
System.out.println();
}
}
}
public List<ContentItem> getContentItems() {
return contentItems;
}
}
ContentCatalog Design Review
The collection type:
List<ContentItem>
Actual objects হতে পারে:
VideoLesson
ArticleLesson
QuizLesson
Common operations:
publish()
calculateEstimatedMinutes()
getContentType()
type checks ছাড়াই call করা হয়েছে।
Optional download capability-এর জন্য:
instanceof Downloadable
ব্যবহার করা হয়েছে।
এটি concrete class check নয়।
এটি capability check।
Why the Catalog Does Not Grade Quizzes
Weak catalog:
if (
content instanceof QuizLesson quiz
) {
quiz.calculateScore(...);
}
Grading একটি learner-specific operation।
Catalog-এর responsibility:
Organize content
Process common content behavior
Quiz grading আলাদা workflow-এর responsibility হওয়া উচিত।
Part 7: Create Completion Policies
Completion content inheritance hierarchy-এর অংশ নয়।
এটি learner progress-এর rule।
Different course বা content-এর completion requirement different হতে পারে।
আমরা composition ব্যবহার করব।
CompletionPolicy.java
Path:
src/main/java/io/liveklass/progress/CompletionPolicy.java
package io.liveklass.progress;
public interface CompletionPolicy {
boolean isCompleted(
int progressPercentage
);
String getDescription();
}
FullCompletionPolicy.java
Path:
src/main/java/io/liveklass/progress/FullCompletionPolicy.java
package io.liveklass.progress;
public final class FullCompletionPolicy
implements CompletionPolicy {
@Override
public boolean isCompleted(
int progressPercentage
) {
return progressPercentage
== 100;
}
@Override
public String getDescription() {
return "Requires 100% progress";
}
}
ThresholdCompletionPolicy.java
Path:
src/main/java/io/liveklass/progress/ThresholdCompletionPolicy.java
package io.liveklass.progress;
public final class ThresholdCompletionPolicy
implements CompletionPolicy {
private final int requiredPercentage;
public ThresholdCompletionPolicy(
int requiredPercentage
) {
if (
requiredPercentage <= 0
|| requiredPercentage > 100
) {
throw new IllegalArgumentException(
"Required percentage must be between 1 and 100."
);
}
this.requiredPercentage =
requiredPercentage;
}
@Override
public boolean isCompleted(
int progressPercentage
) {
return progressPercentage
>= requiredPercentage;
}
@Override
public String getDescription() {
return "Requires at least "
+ requiredPercentage
+ "% progress";
}
}
Why Use Composition Here?
Weak inheritance approach:
FullCompletionProgress
ThresholdCompletionProgress
VideoProgress
ArticleProgress
QuizProgress
Completion rule এবং content type combine করলে subclass combinations বাড়তে পারে।
Composition:
LearnerProgress has a CompletionPolicy
allows:
new FullCompletionPolicy()
অথবা:
new ThresholdCompletionPolicy(
80
)
without changing learner progress class।
Part 8: Implement LearnerProgress
Path:
src/main/java/io/liveklass/progress/LearnerProgress.java
package io.liveklass.progress;
import io.liveklass.content.ContentItem;
public final class LearnerProgress {
private final String learnerName;
private final ContentItem content;
private final CompletionPolicy completionPolicy;
private int progressPercentage;
public LearnerProgress(
String learnerName,
ContentItem content,
CompletionPolicy completionPolicy
) {
if (
learnerName == null
|| learnerName.isBlank()
) {
throw new IllegalArgumentException(
"Learner name is required."
);
}
if (content == null) {
throw new IllegalArgumentException(
"Content is required."
);
}
if (completionPolicy == null) {
throw new IllegalArgumentException(
"Completion policy is required."
);
}
this.learnerName =
learnerName.strip();
this.content = content;
this.completionPolicy =
completionPolicy;
this.progressPercentage = 0;
}
public boolean updateProgress(
int newProgressPercentage
) {
if (
newProgressPercentage < 0
|| newProgressPercentage > 100
) {
return false;
}
if (
newProgressPercentage
< progressPercentage
) {
return false;
}
progressPercentage =
newProgressPercentage;
return true;
}
public boolean isCompleted() {
return completionPolicy
.isCompleted(
progressPercentage
);
}
public String createSummary() {
return learnerName
+ " completed "
+ progressPercentage
+ "% of "
+ content.getTitle()
+ ". "
+ completionPolicy
.getDescription()
+ ".";
}
public String getLearnerName() {
return learnerName;
}
public ContentItem getContent() {
return content;
}
public int getProgressPercentage() {
return progressPercentage;
}
}
Delegation in LearnerProgress
public boolean isCompleted() {
return completionPolicy
.isCompleted(
progressPercentage
);
}
LearnerProgress নিজে completion formula জানে না।
এটি policy-কে operation delegate করে।
Same class different completion rules support করে।
Part 9: Define the Notification Dependency
NotificationSender.java
Path:
src/main/java/io/liveklass/notification/NotificationSender.java
package io.liveklass.notification;
public interface NotificationSender {
boolean send(
String recipient,
String message
);
}
ConsoleNotificationSender.java
Path:
src/main/java/io/liveklass/notification/ConsoleNotificationSender.java
package io.liveklass.notification;
public final class ConsoleNotificationSender
implements NotificationSender {
@Override
public boolean send(
String recipient,
String message
) {
if (
recipient == null
|| recipient.isBlank()
) {
return false;
}
if (
message == null
|| message.isBlank()
) {
return false;
}
System.out.println(
"[Publication Notification]"
);
System.out.println(
"Recipient: "
+ recipient
);
System.out.println(
"Message: "
+ message
);
return true;
}
}
FakeNotificationSender.java
Path:
src/main/java/io/liveklass/notification/FakeNotificationSender.java
package io.liveklass.notification;
public final class FakeNotificationSender
implements NotificationSender {
private String lastRecipient;
private String lastMessage;
private int sendCount;
@Override
public boolean send(
String recipient,
String message
) {
lastRecipient = recipient;
lastMessage = message;
sendCount++;
return true;
}
public String getLastRecipient() {
return lastRecipient;
}
public String getLastMessage() {
return lastMessage;
}
public int getSendCount() {
return sendCount;
}
}
Part 10: Inject the Notification Dependency
Path:
src/main/java/io/liveklass/publishing/ContentPublicationNotifier.java
package io.liveklass.publishing;
import io.liveklass.content.ContentItem;
import io.liveklass.notification.NotificationSender;
public final class ContentPublicationNotifier {
private final NotificationSender sender;
public ContentPublicationNotifier(
NotificationSender sender
) {
if (sender == null) {
throw new IllegalArgumentException(
"Notification sender is required."
);
}
this.sender = sender;
}
public boolean notifyPublication(
ContentItem content,
String recipient
) {
if (content == null) {
return false;
}
if (
recipient == null
|| recipient.isBlank()
) {
return false;
}
if (!content.isPublished()) {
return false;
}
String message =
content.getContentType()
+ " published: "
+ content.getTitle();
return sender.send(
recipient.strip(),
message
);
}
}
Why the Notifier Does Not Publish Content
The class is named:
ContentPublicationNotifier
Its responsibility:
Already-published content-এর notification পাঠানো
It does not:
- Validate content readiness
- Change publication state
- Create notification implementation
- Manage content catalog
Publication remains ContentItem behavior।
Notification remains notifier behavior।
Constructor Injection
public ContentPublicationNotifier(
NotificationSender sender
)
Caller can inject:
ConsoleNotificationSender
or:
FakeNotificationSender
Notifier code unchanged থাকে।
Part 11: Build the Application
Path:
src/main/java/io/liveklass/Main.java
package io.liveklass;
import io.liveklass.catalog.ContentCatalog;
import io.liveklass.content.ArticleLesson;
import io.liveklass.content.QuizLesson;
import io.liveklass.content.VideoLesson;
import io.liveklass.notification.FakeNotificationSender;
import io.liveklass.progress.FullCompletionPolicy;
import io.liveklass.progress.LearnerProgress;
import io.liveklass.progress.ThresholdCompletionPolicy;
import io.liveklass.publishing.ContentPublicationNotifier;
import java.util.List;
public class Main {
public static void main(
String[] args
) {
VideoLesson video =
new VideoLesson(
1L,
"Runtime Polymorphism",
"https://cdn.liveklass.io/videos/polymorphism",
18
);
ArticleLesson article =
new ArticleLesson(
2L,
"Programming to Interfaces",
1_200,
"https://cdn.liveklass.io/articles/interfaces.pdf"
);
QuizLesson quiz =
new QuizLesson(
3L,
"Inheritance Assessment",
10,
70
);
ContentCatalog catalog =
new ContentCatalog(
List.of(
video,
article,
quiz
)
);
int publishedCount =
catalog.publishAll();
catalog.printSummaries();
System.out.println(
"Published items: "
+ publishedCount
);
System.out.println(
"Total learning time: "
+ catalog
.calculateTotalMinutes()
+ " minutes"
);
System.out.println();
System.out.println(
"Available downloads:"
);
catalog.printDownloads();
printQuizResult(
quiz,
8
);
demonstrateCompletionPolicies(
video,
article
);
verifyPublicationNotification(
video
);
}
private static void printQuizResult(
QuizLesson quiz,
int correctAnswers
) {
System.out.println(
"Quiz score: "
+ quiz.calculateScore(
correctAnswers
)
);
System.out.println(
"Quiz passed: "
+ quiz.hasPassed(
correctAnswers
)
);
System.out.println();
}
private static void demonstrateCompletionPolicies(
VideoLesson video,
ArticleLesson article
) {
LearnerProgress videoProgress =
new LearnerProgress(
"Subu",
video,
new FullCompletionPolicy()
);
LearnerProgress articleProgress =
new LearnerProgress(
"Sumu",
article,
new ThresholdCompletionPolicy(
80
)
);
videoProgress.updateProgress(
90
);
articleProgress.updateProgress(
80
);
System.out.println(
videoProgress.createSummary()
);
System.out.println(
"Completed: "
+ videoProgress.isCompleted()
);
System.out.println();
System.out.println(
articleProgress.createSummary()
);
System.out.println(
"Completed: "
+ articleProgress.isCompleted()
);
System.out.println();
}
private static void verifyPublicationNotification(
VideoLesson video
) {
FakeNotificationSender sender =
new FakeNotificationSender();
ContentPublicationNotifier notifier =
new ContentPublicationNotifier(
sender
);
boolean notified =
notifier.notifyPublication(
video,
"nur@example.com"
);
System.out.println(
"Notification sent: "
+ notified
);
System.out.println(
"Send count: "
+ sender.getSendCount()
);
System.out.println(
"Captured recipient: "
+ sender.getLastRecipient()
);
System.out.println(
"Captured message: "
+ sender.getLastMessage()
);
}
}
Expected Output
VIDEO: Runtime Polymorphism
Estimated minutes: 18
Published: true
ARTICLE: Programming to Interfaces
Estimated minutes: 6
Published: true
QUIZ: Inheritance Assessment
Estimated minutes: 20
Published: true
Published items: 3
Total learning time: 44 minutes
Available downloads:
Runtime Polymorphism
https://cdn.liveklass.io/videos/polymorphism
Programming to Interfaces
https://cdn.liveklass.io/articles/interfaces.pdf
Quiz score: 80
Quiz passed: true
Subu completed 90% of Runtime Polymorphism. Requires 100% progress.
Completed: false
Sumu completed 80% of Programming to Interfaces. Requires at least 80% progress.
Completed: true
Notification sent: true
Send count: 1
Captured recipient: nur@example.com
Captured message: VIDEO published: Runtime Polymorphism
Part 12: Compile and Run
Project root থেকে:
javac -d out \
$(find src/main/java -name "*.java")
Run:
java -cp out io.liveklass.Main
Windows PowerShell-এ IDE বা build tool ব্যবহার করা simpler হতে পারে।
Production Java project-এ Maven বা Gradle source discovery এবং compilation manage করে।
Integrated Design Review
Where Is Inheritance Used?
VideoLesson extends ContentItem
ArticleLesson extends ContentItem
QuizLesson extends ContentItem
Reason:
Each one truly is a ContentItem
Where Are Interfaces Used?
Capabilities:
Downloadable
Gradable
Dependencies:
NotificationSender
CompletionPolicy
এগুলো shared base state require করে না।
Where Is Runtime Polymorphism Used?
List<ContentItem>
Loop calls:
content.publish();
content.calculateEstimatedMinutes();
content.getContentType();
Runtime actual implementation execute করে।
Where Is Composition Used?
LearnerProgress
has a ContentItem
LearnerProgress
has a CompletionPolicy
ContentPublicationNotifier
has a NotificationSender
Where Is Delegation Used?
completionPolicy.isCompleted(...)
এবং:
sender.send(...)
Outer class collaboratorকে operation দেয়।
Why Is QuizLesson Final?
Current implementation further inheritance-এর জন্য design করা হয়নি।
Quiz behavior পরিবর্তন করতে configuration বা collaborator ব্যবহার করা যেতে পারে।
Accidental subclassing prevent করা হয়েছে।
Why Does the Catalog Use instanceof?
Download optional capability।
content instanceof Downloadable
asks:
Does this item support downloading?
Duration calculation-এর জন্য কোনো concrete type check ব্যবহার করা হয়নি।
Engineering Note: Publication and Notification Are Separate
Current flow:
Publish content
Then send notification
Notification fail হলেও content published থাকতে পারে।
Production system-এ প্রয়োজন হতে পারে:
- Retry
- Outbox pattern
- Durable event
- Transaction boundary
- Delivery status
- Idempotency
- Failure logging
এই project-এ responsibilities আলাদা রাখা হয়েছে।
A simple boolean full distributed workflow represent করার জন্য যথেষ্ট নয়।
Engineering Note: Completion Policies Can Grow
Current policy শুধু progress percentage ব্যবহার করে।
Real course completion depend করতে পারে:
Required lessons
Quiz passing score
Assignment submission
Attendance
Minimum watch time
Instructor approval
তখন contract evolve হতে পারে:
boolean isCompleted(
CompletionContext context
);
কিন্তু current requirements-এর জন্য simple integer contract যথেষ্ট।
অতিরিক্ত future complexity আগে থেকে introduce করা হয়নি।
Part 13: Required Extension Tasks
Reference project run করার পরে নিচের tasks independently complete করুন।
Extension 1: Add AudioLesson
Requirements:
- Extends
ContentItem - Implements
Downloadable - Audio URL required
- Duration positive
- Secure URL required for publication
- Estimated duration equals audio duration
- Content type
AUDIO
Existing ContentCatalog modify করা যাবে না।
Extension 2: Add a Failed Publication
Create:
VideoLesson
with:
http://
URL।
Verify:
publish()
returns:
false
এবং published state false থাকে।
Extension 3: Add MinimumScoreCompletionPolicy
Policy:
A score of at least the configured percentage is required.
Decide whether existing:
CompletionPolicy
contract score এবং progress দুটো accurately represent করে।
If not, explain what richer context is needed।
Extension 4: Add ConsoleNotificationSender
Inject it into:
ContentPublicationNotifier
Notifier class modify করা যাবে না।
Extension 5: Prevent Duplicate IDs
ContentCatalog constructor-এ duplicate content IDs reject করুন।
Think about:
- Nested loops
Set- Equality
- Error message
Collections এবং generics বিস্তারিতভাবে পরে শেখানো হতে পারে।
Extension 6: Add a Gradable Processing Method
Write:
static void printGrade(
Gradable gradable,
int correctAnswers
)
Method concrete QuizLesson type-এর ওপর depend করবে না।
Part 14: Concept Assessment
প্রতিটি statement True অথবা False নির্ধারণ করুন।
Question 1
An abstract class can contain constructors.
Question 2
A concrete child may ignore an inherited abstract method.
Question 3
A class can extend multiple classes in Java.
Question 4
A class can implement multiple interfaces.
Question 5
Overridden instance methods are selected using the runtime object type.
Question 6
Static methods use the same runtime dispatch as overridden instance methods.
Question 7
A final method can be overridden by a child.
Question 8
A parent reference can hold a child object.
Question 9
Every ContentItem must implement Downloadable.
Question 10
Constructor injection makes a required dependency explicit.
Question 11
Composition can replace every valid use of inheritance without trade-offs.
Question 12
Repeated concrete type checks may indicate missing polymorphic behavior.
Concept Assessment Answers
Answer 1
True
Abstract class constructor child object-এর parent portion initialize করে।
Answer 2
False
Concrete child required abstract methods implement করবে।
Answer 3
False
Java single class inheritance support করে।
Answer 4
True
Answer 5
True
Answer 6
False
Static method selection compile-time class বা reference type-এর ওপর based।
Answer 7
False
Answer 8
True
Answer 9
False
Only downloadable content types interface implement করে।
Answer 10
True
Answer 11
False
Composition-এরও conceptual এবং configuration cost আছে।
Answer 12
True
Part 15: Predict the Output
Question 1
ContentItem content =
new VideoLesson(
1L,
"Video",
"https://example.com/video",
12
);
System.out.println(
content
.calculateEstimatedMinutes()
);
Question 2
ContentItem content =
new QuizLesson(
1L,
"Quiz",
10,
70
);
System.out.println(
content instanceof Downloadable
);
Question 3
CompletionPolicy policy =
new ThresholdCompletionPolicy(
80
);
System.out.println(
policy.isCompleted(
79
)
);
System.out.println(
policy.isCompleted(
80
)
);
Question 4
LearnerProgress progress =
new LearnerProgress(
"Nur",
content,
new FullCompletionPolicy()
);
progress.updateProgress(
70
);
boolean updated =
progress.updateProgress(
60
);
System.out.println(updated);
System.out.println(
progress.getProgressPercentage()
);
Question 5
ContentItem content =
new VideoLesson(
1L,
"Video",
"http://example.com/video",
10
);
System.out.println(
content.publish()
);
System.out.println(
content.isPublished()
);
Predict the Output Answers
Answer 1
12
Runtime VideoLesson implementation execute হয়।
Answer 2
false
QuizLesson Downloadable implement করে না।
Answer 3
false
true
Answer 4
false
70
Progress backward নেওয়া reject হয়।
Answer 5
false
false
URL secure নয়, তাই readiness check fail করে।
Part 16: Find the Design Problem
Problem 1
public class Course
extends ArrayList<ContentItem> {
}
Issue
Course একটি collection implementation নয়।
Inherited operations domain rules bypass করতে পারে।
Better Direction
public final class Course {
private final List<ContentItem> contentItems;
}
Problem 2
public abstract class ContentItem {
public abstract String getDownloadUrl();
}
QuizLesson:
@Override
public String getDownloadUrl() {
throw new UnsupportedOperationException();
}
Issue
Parent contract too broad।
Better Direction
Downloadable
capability interface।
Problem 3
public final class ContentPublicationNotifier {
private final NotificationSender sender =
new ConsoleNotificationSender();
}
Issue
Dependency hardcoded।
Testing এবং replacement difficult।
Better Direction
Constructor injection।
Problem 4
public class FreeCourse
extends Course {
}
public class TenPercentDiscountCourse
extends Course {
}
public class TwentyPercentDiscountCourse
extends Course {
}
Issue
Configuration values subclasses দিয়ে represented।
Subclass count বাড়বে।
Better Direction
PricingPolicy
composition।
Problem 5
public abstract class ContentItem {
protected String title;
protected boolean published;
}
Issue
Children raw state modify করে parent invariants bypass করতে পারে।
Better Direction
Private fields এবং controlled methods।
Part 17: Design Judgment Assessment
প্রতিটি প্রশ্নের উত্তর reasoningসহ দিন।
Question 1
Should VideoLesson extend ContentItem?
Strong Reasoning
হ্যাঁ, যদি:
- Video lesson সত্যিই content item
- Parent contract completeভাবে fulfil করে
- Shared ID, title এবং publication lifecycle meaningful
- Hierarchy shallow থাকে
Question 2
Should QuizLesson implement Downloadable and return an empty URL?
Strong Reasoning
না, যদি quiz download support না করে।
Unsupported capability interface implement করা উচিত নয়।
Question 3
Should every service have an interface?
Strong Reasoning
না।
Interface useful যখন:
- Multiple implementations meaningful
- External boundary আছে
- Tests replacement require করে
- Caller behavior contract-এর ওপর depend করে
Mechanical one-interface-per-class unnecessary।
Question 4
Should completion percentage be implemented with subclasses of LearnerProgress?
Strong Reasoning
সাধারণত না।
Completion rule independently varies।
LearnerProgress has a CompletionPolicy more flexible।
Question 5
Should ContentCatalog know how a quiz score is calculated?
Strong Reasoning
না।
Score calculation Gradable implementation-এর responsibility।
Catalog common content operations manage করে।
Part 18: Final Implementation Challenge
Reference code না দেখে একটি simplified system তৈরি করুন।
Required types:
LearningResource
VideoResource
DocumentResource
AssessmentResource
Downloadable
Gradable
ResourceCatalog
NotificationSender
ResourceNotifier
Rules:
LearningResourceabstract- Shared
id,title, publication state - Every resource estimated duration calculate করবে
- Video এবং document downloadable
- Assessment gradable
- Catalog polymorphic list ব্যবহার করবে
- Notifier constructor injection ব্যবহার করবে
- No concrete type condition for duration calculation
- Unsupported methods parent class-এ থাকবে না
- Raw mutable parent fields protected হবে না
Final Challenge Evaluation
নিজের implementation evaluate করুন।
0 = Missing or incorrect
1 = Partially correct
2 = Correct and intentional
| Area | Score |
|---|---|
| Valid inheritance relationship | /2 |
| Abstract parent used appropriately | /2 |
| Parent state private | /2 |
| Parent constructor validates state | /2 |
| Abstract methods implemented | /2 |
Overriding uses @Override | /2 |
| Runtime polymorphism demonstrated | /2 |
| Polymorphic collection used | /2 |
| Capability interfaces focused | /2 |
| Unsupported parent methods avoided | /2 |
| Constructor injection used | /2 |
| Fake implementation possible | /2 |
| Composition used for variable behavior | /2 |
| No unnecessary type checks | /2 |
| Hierarchy remains shallow | /2 |
Maximum:
30
Interpretation:
26–30 → Strong understanding
21–25 → Good foundation
15–20 → Review design decisions
Below 15 → Rebuild the practice project
Module Completion Checklist
Module 3 complete করার আগে নিশ্চিত করুন আপনি পারবেন:
-
extendsব্যবহার করতে - Valid
is-arelationship identify করতে -
super(...)দিয়ে parent constructor call করতে - Constructor execution order explain করতে
- Method overriding করতে
-
@Overrideব্যবহার করতে - Runtime method dispatch explain করতে
- Overriding এবং overloading distinguish করতে
- Static method hiding explain করতে
- Fields polymorphic নয় বুঝতে
- Abstract class তৈরি করতে
- Abstract method declare করতে
- Interface implement করতে
- Multiple interfaces ব্যবহার করতে
- Parent/interface type parameter লিখতে
- Polymorphic collection process করতে
- Safe capability checks করতে
- Constructor injection implement করতে
- Fake dependency দিয়ে behavior verify করতে
- Inheritance misuse identify করতে
- Composition এবং delegation ব্যবহার করতে
- Shallow, meaningful hierarchy design করতে
Module Summary
এই module-এ আমরা শিখেছি:
- Inheritance common type hierarchy তৈরি করে
extendsparent-child relationship declare করে- Valid inheritance meaningful
is-arelationship require করে - Child parent contract safely fulfil করতে হবে
- Parent constructor child-এর আগে execute হয়
super(...)parent state initialize করে- Parent private fields child direct access করতে পারে না
- Excessive
protectedstate encapsulation দুর্বল করে - Method overriding specialized behavior provide করে
@Overridesignature mistakes detect করে- Overridden instance methods runtime-dispatched
- Static methods runtime polymorphic নয়
- Fields runtime polymorphic নয়
- Abstract class incomplete common type define করে
- Abstract method concrete childrenকে behavior implement করতে বাধ্য করে
- Interface capability এবং behavior contract express করে
- A class one parent extend এবং multiple interfaces implement করতে পারে
- Parent/interface parameters reusable methods তৈরি করে
- Polymorphic collections different implementations process করে
- Optional capability detect করতে
instanceofreasonable হতে পারে - Repeated concrete type conditions missing polymorphism indicate করতে পারে
- Programming to interfaces implementation coupling কমায়
- Constructor injection required dependencies explicit করে
- Composition root implementations select করে
- Fake implementations isolated testing support করে
- Inheritance শুধু code reuse-এর tool নয়
- Unsupported parent behavior hierarchy problem indicate করে
- Configuration-based subclasses subclass explosion তৈরি করতে পারে
- Composition variable behavior independently combine করতে পারে
- Delegation collaboratorকে operation সম্পন্ন করতে দেয়
- Compositionও overused হতে পারে
- Strong design simplest correct abstraction বেছে নেয়
- Inheritance, interface এবং composition complementary tools
Module Complete
আপনি এখন Java-তে inheritance syntax শুধু ব্যবহার করতে নয়, hierarchy design evaluate করতেও প্রস্তুত।
Strong design-এর central প্রশ্ন:
Is this truly a type relationship?
তারপর:
Can every child safely fulfil the parent contract?
যদি উত্তর না হয়, inheritance-এর পরিবর্তে interface, composition, delegation, configuration বা simple field stronger হতে পারে।