Use a customer object with no selected platforms as the base component, and wrap it once for each selected channel. Each decorator object has the same interface, delegates existing work, adds one platform send, and adds that platform's per-message charge. The code below uses the
Customer and Comms interfaces from the question; every decorator receives the communication implementation explicitly.final class NoChannels(val name: String) extends Customer {
def spread(msg: String): Unit = ()
def cost: Double = 0.0
}
abstract class CustomerDecorator(protected val inner: Customer)
extends Customer {
final val name: String = inner.name
def spread(msg: String): Unit = inner.spread(msg)
def cost: Double = inner.cost
}
final class OnDiscord(c: Customer, out: Comms)
extends CustomerDecorator(c) {
override def spread(msg: String): Unit = {
super.spread(msg)
out.sendD(name, msg)
}
override def cost: Double = super.cost + 0.01
}
final class OnFacebook(c: Customer, out: Comms)
extends CustomerDecorator(c) {
override def spread(msg: String): Unit = {
super.spread(msg)
out.sendF(name, msg)
}
override def cost: Double = super.cost + 0.05
}
final class OnWhatsApp(c: Customer, out: Comms)
extends CustomerDecorator(c) {
override def spread(msg: String): Unit = {
super.spread(msg)
out.sendW(name, msg)
}
override def cost: Double = super.cost + 0.03
}
def customer(name: String, discord: Boolean, facebook: Boolean,
whatsapp: Boolean, out: Comms): Customer = {
var c: Customer = new NoChannels(name)
if (discord) c = new OnDiscord(c, out)
if (facebook) c = new OnFacebook(c, out)
if (whatsapp) c = new OnWhatsApp(c, out)
c
}The factory admits each platform at most once, matching a subset of preferences. For example,
customer("reader", true, false, true, out) sends once to Discord and once to WhatsApp and has cost per message, using the stipulated charges. The innermost wrapper executes first, giving deterministic Discord–Facebook–WhatsApp order for selected channels. The zero-platform case sends nothing and costs zero. Repeated messages reuse the same immutable choice of wrappers.By structural induction on the wrapper chain, the base sends to the empty subset with zero cost; one additional wrapper preserves all prior sends and adds exactly its channel and charge. Thus the sends and charge are composed from the same selected components. There are channel-specific classes instead of one class per subset, and a new channel needs one new wrapper. A direct manual construction containing the same wrapper twice would intentionally send and charge twice, so the factory prevents that configuration.
cost computes a per-message amount rather than charging by itself; a later payment mechanism can use it. The interface uses Double, so numerical tests should allow ordinary floating-point rounding. Articles by others on the same topic
There are currently no matching articles.