vendredi 31 décembre 2021

How to avoid typecasting to subclass when using null object pattern

I have a Value interface with a method to show value as a string. Usually the value is an integer so IntegerValue implements Value. Sometimes value is unknown which I use null object pattern for so UnknownValue implements Value.

When the value is actually an integer, it's useful for the client to check whether the value is high enough (IntegerValue.isEnough). This affects how this value is displayed to the user later on. However, if the value is unknown, it doesn't make sense to check if it's high enough--the value is unknown. By the Interface Segregation Principle, UnknownValue should not have an isEnough method.

interface Value {
  toString(): string;
}

class IntegerValue implements Value {
  private value: number;
  constructor(v: number) { this.value = v }
  isEnough() { return this.value >= 30 }
  toString() { return '' + this.value }
}

class UnknownValue implements Value {
  toString() { return 'unknown' }
}

But the client accesses a Value and won't know whether it's an IntegerValue. So I'd have to check and then typecast it.

if(value.toString() !== 'unknown') {
  handleInteger(value as IntegerValue) // <-- check if isEnough inside
} else {
  handleUnknown(value)
}

I was wondering if there was a design pattern that could solve this with polymorphism, without typecasting.

I was considering the Visitor Pattern like so:

interface ValueVisitor {
  handleInteger(v: IntegerValue): void;
  handleUnknown(): void
}

class ViewValueVisitor implements ValueVisitor { ... }
class JsonSerializerValueVisitor implements ValueVisitor { ... }

interface Value {
  toString(): string;
  acceptVisitor(v: ValueVisitor): void;
}

class IntegerValue implements Value {
  ...
  acceptVisitor(v) { v.handleInteger(this) }
}

class UnknownValue implements Value { 
  ...
  acceptVisitor(v) { v.handleUnknown() }
}

But the Visitor Pattern violates the Open Closed Principle. I was wondering if there is a better solution.

Aucun commentaire:

Enregistrer un commentaire