空对象模式
外观
在面向对象编程中,空对象(英語:null object),是不具有被引用的值或具有已定义的中性(null)行为的对象。空对象设计模式,描述了这种对象的用法和它们的行为,它最初被描述为“空值”(Void值)[1] ,后来在程序设计的模式语言丛书中被描述为“空对象”[2]。
示例
[编辑]C++
[编辑]作为具有到对象的静态类型引用的语言,C++诠释了为何空对象成为了一种复杂模式:
import std;
using std::unique_ptr;
class Animal {
public:
virtual ~Animal() = default;
virtual void makeSound() const = 0;
};
class Dog: public Animal {
public:
virtual void makeSound() const override {
std::println("Woof!");
}
};
class NullAnimal: public Animal {
public:
virtual void makeSound() const override {
// silence...
}
};
int main() {
unique_ptr<Animal> dog = std::make_unique<Dog>();
dog->makeSound(); // outputs "Woof!"
unique_ptr<Animal> unknown = std::make_unique<NullAnimal>();
unknown->makeSound(); // outputs nothing, but does not throw a runtime exception
}
这里的想法是有一些需要到一个Animal对象的指针或引用的场合,但却不能得到适合的对象。在遵循标准的C++中空引用是不可能的。空Animal*指针是可能的,它可以被用为占位符,但不能用于直接分派:a->makeSound()在a是空指针的情况下有着未定义的行为。
空对象模式通过提供特殊的NullAnimal类来解决这个问题,这个类可以被实例化而绑定到有一个Animal指针或引用。必须为每个有空对象的类层级创建一个特殊的空类。在C++中,函数或方法可以显式的声明其参数是否允许为空:
// Function which requires an Animal instance, and will not accept null.
void doSomething(const Animal& animal) {
// animal may never be null here.
}
// Function which may accept an Animal instance or null.
void doSomething(const Animal* animal) {
// animal may be null.
}
Java
[编辑]下面是Java的例子:
package org.wikipedia.examples;
interface Animal {
void makeSound();
}
class Dog implements Animal {
public void makeSound() {
System.out.println("Woof!");
}
}
class NullAnimal implements Animal {
public void makeSound() {
// silence...
}
}
public class Example {
public static void main(String[] args) {
Animal dog = new Dog();
dog.makeSound(); // outputs "Woof!"
Animal unknown = new NullAnimal();
unknown.makeSound(); // outputs nothing, but does not throw a runtime exception
}
}
C#
[编辑]C#是可以真正实现空对象模式的语言。这个例子展示了会叫唤的动物对象,并将C#的null关键字替代为NullAnimal的一个实例Null:
namespace Wikipedia.Examples;
// Null object pattern implementation:
using System;
// Animal interface is the key to compatibility for Animal implementations below.
interface IAnimal {
void MakeSound();
}
// Animal is the base case.
abstract class Animal : IAnimal {
// A shared instance that can be used for comparisons
public static readonly IAnimal Null = new NullAnimal();
// The Null Case: this NullAnimal class should be used in place of C# null keyword.
private class NullAnimal : Animal {
public override void MakeSound() {
// Purposefully provides no behaviour.
}
}
public abstract void MakeSound();
}
// Dog is a real animal.
class Dog : Animal {
public override void MakeSound() {
Console.WriteLine("Woof!");
}
}
/*
* Simplistic usage example in a Main entry point.
*/
static class Program {
static void Main() {
IAnimal dog = new Dog();
dog.MakeSound(); // outputs "Woof!"
/* Instead of using C# null, use the Animal.Null instance.
* This example is simplistic but conveys the idea that if the Animal.Null instance is used then the program
* will never experience a .NET System.NullReferenceException at runtime, unlike if C# null were used.
*/
IAnimal unknown = Animal.Null; //<< replaces: IAnimal unknown = null;
unknown.MakeSound(); // outputs nothing, but does not throw a runtime exception
}
}
参见
[编辑]引用
[编辑]- ^ Kühne, Thomas. Proceedings of the First International Conference on Object-Oriented Technology, White Object-Oriented Nights 1996 (WOON'96), St. Petersburg, Russia. 1996.
- ^ Woolf, Bobby. Null Object. Martin, Robert; Riehle, Dirk; Buschmann, Frank (编). Pattern Languages of Program Design 3. Addison-Wesley. 1998.