在OOP中向现有类添加功能

Adding functionality to an existing class in OOP

本文关键字:添加 功能 OOP      更新时间:2023-10-16

在面向对象编程中,我们经常使用多态性来使几种不同类型的对象在同一个"接口"下表现。面包和黄油的例子是这样的:

public abstract class Animal {
    public abstract void makeSound();
}
public class Dog extends Animal {
    public void makeSound() {
        System.out.println("Woof woof");
    }
}
public class Cat extends Animal {
    public void makeSound() {
        System.out.println("Meow");
    }
}

我们不关心实际的动物是什么,只关心它会发出声音。

然而,当我们想要向现有类添加新特性时,问题就出现了。在我的情况下,我试图使几个类实现UniformProvider接口(它将类转换为OpenGL着色器程序的原始统一值)。

到目前为止,我已经使用了一个简单的instanceof检查,处理了许多可能的类型,但是我不想依赖instanceof,因为它不容易扩展而不修改源文件。

UniformProvider接口的问题是它不能实现内置类型,如float(或Float), int(或Integer)和对象数组(例如:Matrix4f[])。此外,我认为将统一变量转换的功能放入数学类违反了单一职责原则。

在Rust语言中,结构体有一个有用的特性,我现在才意识到它的潜力。在传统的OOP中,类/结构实现接口/trait,如果不修改原文件,就无法实现更多的接口。然而,在Rust中,为结构提供并实现了trait ,从而逆转了依赖关系。它使用类似impl Add for Vector4f的语法而不是Vector4f implements Add

我正在考虑在传统的面向对象语言(如Java或c++)中存在哪些选项来实现这种模式。我考虑创建一个转换器的注册表,它被分配给一个类:例如floatFloatConverter,然后使用HashMap或等效的方法访问相关的转换器,但我认为这可能是相当低效的。

我问这个困境有什么解决方案(或者我是否应该切换到Rust:)[我正在考虑])。

一个优雅的解决方案是访问者模式

// Base object hierarchy
abstract class Animal {
  abstract void Accept(AnimalVisitor visitor);
}
class Dog extends Animal {
  void Accept(AnimalVisitor visitor) { visitor.Visit(this); }
}
class Cat extends Animal {
   void Accept(AnimalVisitor visitor) { visitor.Visit(this); }
}
interface AnimalVisitor {
  Visit(Dog dog);
  Visit(Cat cat);
}
// Here is a concrete visitor ...
class AnimalNoiseMaker implements AnimalVisitor {
  Visit(Dog dog) { System.out.println("Woof woof"); }
  Visit(Cat cat) { System.out.println("Meow"); }
}
// .. and how you use it
Animal a = ...;
a.Accept(new AnimalNoiseMaker()); // Woof or Meow !

当我无法修改现有类时,我经常使用decorator模式向现有类添加行为:

public final class EmpoweredAnimal extends Animal
{
    private final Animal animal;
    public EmpoweredAnimal(Animal animal)
    {
        this.animal = animal;
    }
    @Override
    public final void makeSound()
    {
        this.animal.makeSound();
    }
    public final void jump()
    {
        // TODO
    }
}

用法:

Animal myDog = new Dog();
//Drawback: you must base your polymorphism on the EmpoweredAnimal now
EmpoweredAnimal myEmpoweredDog = new EmpoweredAnimal(myDog);
myEmpoweredDog.makeSound(); //prints "woof woof"
myEmpoweredDog.jump(); //do something new

这个缺点是可以接受的,如果你所有的动物都必须被授权,否则你被迫继续使用instanceof,因为不是所有的Animal都会被包装在一个EmpoweredAnimal中。