The prototype pattern is a creational pattern: a client asks a configured object to clone itself through a common object interface, and concrete prototypes implement the cloning operation. The client can thereby create objects without naming their concrete classes at every creation site.
Use it when the required configuration or concrete type is chosen at runtime, or when reconstructing expensive initial state is less convenient than copying a prepared example. A deep copy may be needed for independently mutable nested data; merely duplicating outer fields can leave reference aliasing. The cloning contract determines what is copied and what may safely be shared.
This is the decorator pattern, a structural pattern.
BasicPlan supplies the initial component. Packages stores another CableTV behind the same interface and delegates to it; Movies and HDChannels add their own price and description. Wrapping one package around another combines the optional responsibilities without constructing a subclass for each possible combination.The decorator pattern participants are:
- Component:
CableTVdeclares the commoncostandpackagesqueries, so clients can use both basic and decorated accounts through one object interface. - Concrete component:
BasicPlansupplies the initial price and package description. - Decorator: the abstract
Packagesclass stores a component reference and forwards its operations, enabling recursive object composition. - Concrete decorators:
MoviesandHDChannelsoverride the queries to add one responsibility while preserving the wrapped component's behavior. - Client: an application chooses the nesting of components and observes the resulting account through
CableTV.
Each wrapper contributes its price exactly once along the chain. The same structural arrangement handles a new optional package without changing the existing component interface.
Add one concrete decorator and a small application:
class Sports(account: CableTV) extends Packages(account) {
override def cost: Double = super.cost + 5.0
override def packages: String = super.packages + ", Sports"
}
object CableTVExample extends App {
val basic: CableTV = new BasicPlan
val movies: CableTV = new Movies(basic)
val hd: CableTV = new HDChannels(movies)
val sports: CableTV = new Sports(hd)
Seq(basic, movies, hd, sports).foreach { account =>
println(account.packages + " = £" + account.cost)
}
}The output is:
Basic plan = £30.0
Basic plan, Movies = £40.0
Basic plan, Movies, HD channels = £47.0
Basic plan, Movies, HD channels, Sports = £52.0The final price is £52. Each new wrapper leaves earlier account objects available at their original prices. In the UML class diagram, both
BasicPlan and Packages realize CableTV; each package class inherits Packages. The account relationship points from the decorator to another CableTV component. It is an association, without asserting exclusive lifetime ownership.Cable TV decorator classes extended with the Sports package
. Original UML class diagram. Dashed hollow-triangle arrows denote interface realization; solid hollow-triangle arrows denote class inheritance. The account association permits nested decorators. Details.A sequence diagram shows participants as lifelines and the order of messages between them, usually with time progressing downward. It can show a client asking a sports wrapper for its price, followed by nested calls through the other decorators to the basic component, then replies carrying the accumulated cost.
A state diagram shows the possible states of a system and event-triggered transitions, with optional guards and actions. For an account, it might distinguish active, suspended and closed states and the events allowing a change.
Both are behavioral UML views, but a sequence diagram emphasizes ordered interactions between participants; a state diagram emphasizes states and permitted transitions. A lifecycle state diagram can admit many different message traces.
Articles by others on the same topic
There are currently no matching articles.
