Inheritance, Interfaces, and Polymorphism

Project: Polymorphic content system

ReadingPreview

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:

  1. Already published check
  2. Readiness check
  3. 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:

  • LearningResource abstract
  • 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
AreaScore
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-a relationship 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 তৈরি করে
  • extends parent-child relationship declare করে
  • Valid inheritance meaningful is-a relationship require করে
  • Child parent contract safely fulfil করতে হবে
  • Parent constructor child-এর আগে execute হয়
  • super(...) parent state initialize করে
  • Parent private fields child direct access করতে পারে না
  • Excessive protected state encapsulation দুর্বল করে
  • Method overriding specialized behavior provide করে
  • @Override signature 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 করতে instanceof reasonable হতে পারে
  • 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 হতে পারে।