最令人头痛的问题。因此在C++中应尽可能地使用前置声明(forward declaration),减少包含的(included)头文件。另外,可以将一些私有静态(private static)成员从头文件转移到实现代码中,以匿名命名空间(anonymous namespace)的方式来实现完全隐藏。此外还有一个非常有用的技巧——柄/体(handle/body)模式或称桥梁模式(bridge pattern),可以将接口与实现完全分开。这种模式不仅可以解决C++中的头文件问题,对Java等不需要头文件的语言也是有用的。下面我们用这种模式重新实现Complex类。”
幻灯一闪,新的源码出现在众人眼前——
// 复数计算接口ComplexImpl
public interface ComplexImpl
{
public double real();
public double imaginary();
public double modulus();
public double argument();
public Complex add(Complex other);
public Complex multiply(Complex other);
}
// 用直角坐标实现的ComplexImpl
public class ComplexCartesianImpl implements ComplexImpl
{
private double x;
private double y;
public ComplexCartesianImpl(double x, double y)
{
this.x = x;
this.y = y;
}
public double real() { return x; }
public double imaginary() { return y; }
public double modulus() { return StrictMath.hypot(x, y); }
public double argument() { return StrictMath.atan2(y, x); }
public Complex add(Complex other)
{
return new Complex(x + other.real(), y + other.imaginary());
}
public Complex multiply(Complex other)
{
return new Complex(x * other.real() - y * other.imaginary(),
x * other.imaginary() + y * other.real());
}
}
// 用极坐标实现的ComplexImpl
public class ComplexPolarImpl implements ComplexImpl
{
private double r;
private double theta;
// 以下省略。。。
}
// 用桥梁模式实现的复数类
public class Complex
{
private ComplexImpl impl;
public Complex(double x, double y)
{
impl = new ComplexCartesianImpl(x, y);
//或者:impl = new ComplexPolarImpl(x, y);
}
public double real() { return impl.real(); }
public double imaginary() { return impl.imaginary(); }
public double modulus() { return impl.modulus(); }
public double argument() { return impl.argument(); }
public Complex add(Complex other) { return impl.add(other); }
public Complex multiply(Complex other) { return impl.multiply(other); }
}
冒号进而指出:“这是桥梁模式的简化版。稍加改进,我们不仅可以在编译期间决定具体实现方式,甚至可以让客户在运行期间选择实现方式。你们课后不妨试试。”
引号一拍大腿:“妙!如此既免除了实现者抉择的烦恼,也给赋予使用者更大的自由,可谓一举两得啊。”
句号也道:“信息隐藏虽能将抽象接口与具体实现分离,但仍然封装在同一类中。桥梁模式则让二者彻底解耦(decouple),增强了对变化的适应力,具有更大的灵活性和可扩展性。”
“当然这也增加了一定的复杂性和效率上的损失,具体运用时应酌情考量,避免过度设计。”冒号提醒道,“最后,如果Complex类需要功能上的变化,比如增加乘方、开方等运算,只要不修改现有运算的签名,是不会伤及客户代 |