I plan to use this code as not a learning demo, but a production-ready (all names / comments to be modified of course) code snippet in my work and add it to my snippet database. I would like to verify that it has no mistakes, silly things and maybe lacking something important.
package view;
//Component interface
public interface Car {
void drive();
void stop();
}
//Basic Component (implements component iface, provides basic functionality
//additional functionality will be added by concrete decorator classes).
//Alternative Names: SimpleCar, BasicComponent, MainComponent
public class BasicCar implements Car {
@Override
public void drive() {
System.out.println("Basic Car moving/rolling!");
}
@Override
public void stop() {
// NOP
// real Car surely must implement stop() :))
}
}
//(abstract) Decorator class.
//Adds nothing to interface method.
//will be used as template for all concrete decorator classes
//which would
//1. call ctor of this abstract class
//2. methods overriding drive would call super.drive() + add their functionality
//before or after calling super.drive() - thus modifying.
public abstract class CarDecorator implements Car {
protected final Car carToDecorate; // carBeforeThisDecorator
public CarDecorator(Car car) {
this.carToDecorate = car;
}
@Override
public void drive() {
carToDecorate.drive();
}
@Override
public void stop() {
carToDecorate.stop();
}
}
//concrete Decorator 1.
//Alternative Name: LoudHonkingCar, LoudHonkFeaturedCar, LoudHonkCar,
//WithLoudHonkDecorator, LoudHonkingCarDecorator, LoudHonkCarDecorator
//(thou "Decorator" word is superfluous - "extends Decorator" explains it!)
// Usually adds one feature (LoudHonk) which is described by its class name
// this feature may mean overriding one OR MORE methods of Car interface
public class WithLoudHonk extends CarDecorator {
public WithLoudHonk(Car carToBeDecorated) {
super(carToBeDecorated);
}
// Additional functionality can be harcoded into this method.
// Much better approach is to use private method for this
// additional functionality.
@Override
public void drive() {
// also can add functionality BEFORE BasicCar functionality
// System.out.println("honking loudly BEFORE started to move");
makeHonkingSoundBeforeDrive();
super.drive();
makeHonkingSoundAfterDrive();
// adding functionality AFTER BasicCar functionality
// System.out.println("honking loudly AFTER started to move");
}
// method contains additional functionality
private void makeHonkingSoundBeforeDrive() {
System.out.println("honking loudly BEFORE started to move");
}
// method contains additional functionality
private void makeHonkingSoundAfterDrive() {
System.out.println("honking loudly AFTER started to move");
}
// this class does not add anything to stop() method, but it could!
}
//concrete Decorator 2.
//Alternative Name: SportsFeaturedCar, SportsCar,
//WithSportsFeatureDecorator, SportsCarDecorator
public class WithSportsFeature extends CarDecorator {
public WithSportsFeature(Car carToBeDecorated) {
super(carToBeDecorated);
}
@Override
public void stop() {
makeScreechingSound();
super.stop();
}
private void makeScreechingSound() {
System.out.println("superfast sports car's wheels screeching to stop!");
}
// this class does not add anything to drive() method, but it could!
}
public class Driver {
public static void main(String[] args) {
// no Decorator so far - just using class BasicCar as ususally.
Car mySimpleCar = new BasicCar();
mySimpleCar.drive();
mySimpleCar.stop();
System.out.println("***********************");
// Now using Decorator pattern.
// Some people use abstract Decorator class instead Component iface:
// CarDecorator myCarWithAddedFeatures;
Car myCarWithAddedFeatures;
myCarWithAddedFeatures = new WithSportsFeature(
new WithLoudHonk(mySimpleCar));
myCarWithAddedFeatures.drive();
myCarWithAddedFeatures.stop();
// some construct Decorated car not as one-liner, but step-by-step:
Car myCarWithLoudHonk = new WithLoudHonk(mySimpleCar);
Car myCarWithLoudHonkAndSportsFeature = new WithSportsFeature(
myCarWithLoudHonk);
System.out.println("***********************");
myCarWithLoudHonkAndSportsFeature.drive();
myCarWithLoudHonkAndSportsFeature.stop();
}
}
Just thoght about following: Maybe abstract CarDecorator class shall be made package-private and be put in separate package (than client class - Driver.java), so that Client (Driver class) has no access to abstract CarDecorator ? So that client code can use only concrete decorator classes (WithLoudHonk, ...) so that Client cannot make any conrete decorators himself? But I think this a stupid idea (no reason to disallow doing this to client)...
I will be summarizing all answers here, to later rework my sample honoring them all:
- WithLoudHonk -> LoudHonkingCar (rename class, use word Car in
name, try avoid pronouns ("with") - WithSportsFeature -> SportsFeaturedCar (rename class, same)
- rename methods: makeHonkingSoundBeforeDrive, makeHonkingSoundAfterDrive -> honkBeforeDrive, honkAfterDrive. Just honkBefore(), honkAfter() is not clear BEFORE/AFTER WHAT (drive method meant)? RIGHT? I cannot make method name shorter and clearer, can I?
- adding features shall depend on some condition in RT, otherwise I
could do it in constructor without decorator - so add to Driver
dynamic behavior/logic: "if(condition) { decorate - add features to
Basic car, else if (other condition) { add different features})
- add code to stop() method.
I do appreciate every contribution. Thank you very much!
publickeyword, all interface methods arepublic. \$\endgroup\$