如何构建通常需要一起调用的 类 ?
How can I structure my classes which usually need to be called together?
我有一些实现相同方法的相关 类
class Dog {
public void speak() { System.out.println("Bark") }
}
class Cat {
public void speak() { System.out.println("Meow") }
}
90% 的时间,用户希望狗和猫都能说话。他们不想知道细节。当我们添加一种新动物时,他们会希望它也能说话。这个想法是为了避免:
// All animals need to be explicitly told to speak every time
new Dog().speak();
new Cat().speak();
// But we just added Birds, and the users need to remember to add this call everywhere
new Bird.speak();
我可以做类似的事情
class Animals {
public void speak() {
new Dog().speak();
new Cat().speak();
new Bird().speak();
}
}
以便用户每次都可以调用 new Animals().speak()
。
但是,在 10% 的情况下,它确实需要可配置。我想要的是一种让用户做这样的事情的方法
// Used most of the time
Animals.withAllAnimals().speak();
// Sometimes they don't want cats, and they want the dogs to woof instead
Animals.exclude(Cat)
.configure(Dog.sound, "Woof")
.speak();
我如何构造我的 类 来完成这个?
这里有一些想法。
import java.util.*;
import java.util.function.Consumer;
/**
* An animal can either speak with its own voice, or another supplied
*/
interface Animal {
String speak();
String speak(String voice);
}
/**
* Base class for animal implementations.
*
* An animal is added to a Menagerie when created.
*/
abstract class BaseAnimal implements Animal {
private final String defaultVoice;
public BaseAnimal(Menagerie menagerie, String defaultVoice) {
this.defaultVoice = defaultVoice;
menagerie.add(this);
}
public String speak(String voice) {
return voice;
}
public String speak() {
return speak(defaultVoice);
}
}
/**
* A Dog. Even when given a voice the dog does things slightly differently.
*/
class Dog extends BaseAnimal {
public Dog(Menagerie menagerie) {
super(menagerie, "Bark!");
}
public String speak(String voice) {
return voice + " (and drools)";
}
}
/**
* A collection of animals. We can do something side-effectful to each, or create a new collection where
* some classes of animal are excluded or have different behaviour.
*/
interface Animals {
void forEach(Consumer<Animal> action);
Animals exclude(Class<Animal> clazz);
Animals configureVoice(Class<Animal> clazz, String voice);
}
/**
* An Animals instance which can contain only a single animal of each class
* (an arbitrary decision based on the code in the question)
*/
class Menagerie implements Animals {
Map<Class<? extends Animal>,Animal> animals = new HashMap<>();
public Menagerie() {
}
public Menagerie(Map<Class<? extends Animal>, Animal> animals) {
this.animals = new HashMap<>(animals);
}
public void add(Animal animal) {
animals.put(animal.getClass(), animal);
}
public void forEach(Consumer<Animal> action) {
animals.values().forEach(action);
}
@Override
public Animals exclude(Class<Animal> clazz) {
Menagerie m = new Menagerie(animals);
m.animals.remove(clazz);
return m;
}
/**
* Return an Animals instance which contains a proxy for a particular type of animal
* which will behave differently when speak() is called.
*/
@Override
public Animals configureVoice(Class<Animal> clazz, String voice) {
Menagerie m = new Menagerie(animals);
Animal a = m.animals.get(clazz);
if (a != null) {
m.animals.put(clazz, new Animal() {
@Override
public String speak() {
return voice;
}
@Override
public String speak(String voice) {
return a.speak(voice);
}
});
}
return m;
}
}
我知道这是用 java
标记的问题。但是,让我举一个 C#
的例子,因为这些语言有很多共同点。
第一件事是我会使用继承并创建抽象 class Animal
as common behaviour Speak()
is used。因此,抽象 class 应该定义行为,派生的 classes 应该实现该行为:
public abstract class Animal
{
public abstract void Speak();
}
然后在派生的 classes:
中使用继承和覆盖行为
public class Bird : Animal
{
public override void Speak()
{
System.Console.WriteLine("I am a bird!");
}
}
public class Cat : Animal
{
public override void Speak()
{
System.Console.WriteLine("I am a cat!");
}
}
public class Dog : Animal
{
public override void Speak()
{
System.Console.WriteLine("I am a dog!");
}
}
那么我们需要一个class,让所有的动物都能说话。让我们为此创建 Choir
class:
public class Choir
{
private List<Animal> choristers;
public void AddChoristers(IEnumerable<Animal> choristers)
{
if (choristers == null)
choristers = new List<Animal>();
choristers.AddRange(choristers);
}
public void SpeakAll()
{
foreach (Animal animal in choristers)
animal.Speak();
}
public void Speak(Func<Animal, bool> filter)
{
IEnumerable<Animal> filteredAnimals = choristers
.Where(filter ?? (animal => true));
foreach (Animal animal in filteredAnimals)
animal.Speak();
}
}
注意Speak()
方法。它可以接受一个谓词作为参数,所以你可以选择想要的动物来 speak()
.
我有一些实现相同方法的相关 类
class Dog {
public void speak() { System.out.println("Bark") }
}
class Cat {
public void speak() { System.out.println("Meow") }
}
90% 的时间,用户希望狗和猫都能说话。他们不想知道细节。当我们添加一种新动物时,他们会希望它也能说话。这个想法是为了避免:
// All animals need to be explicitly told to speak every time
new Dog().speak();
new Cat().speak();
// But we just added Birds, and the users need to remember to add this call everywhere
new Bird.speak();
我可以做类似的事情
class Animals {
public void speak() {
new Dog().speak();
new Cat().speak();
new Bird().speak();
}
}
以便用户每次都可以调用 new Animals().speak()
。
但是,在 10% 的情况下,它确实需要可配置。我想要的是一种让用户做这样的事情的方法
// Used most of the time
Animals.withAllAnimals().speak();
// Sometimes they don't want cats, and they want the dogs to woof instead
Animals.exclude(Cat)
.configure(Dog.sound, "Woof")
.speak();
我如何构造我的 类 来完成这个?
这里有一些想法。
import java.util.*;
import java.util.function.Consumer;
/**
* An animal can either speak with its own voice, or another supplied
*/
interface Animal {
String speak();
String speak(String voice);
}
/**
* Base class for animal implementations.
*
* An animal is added to a Menagerie when created.
*/
abstract class BaseAnimal implements Animal {
private final String defaultVoice;
public BaseAnimal(Menagerie menagerie, String defaultVoice) {
this.defaultVoice = defaultVoice;
menagerie.add(this);
}
public String speak(String voice) {
return voice;
}
public String speak() {
return speak(defaultVoice);
}
}
/**
* A Dog. Even when given a voice the dog does things slightly differently.
*/
class Dog extends BaseAnimal {
public Dog(Menagerie menagerie) {
super(menagerie, "Bark!");
}
public String speak(String voice) {
return voice + " (and drools)";
}
}
/**
* A collection of animals. We can do something side-effectful to each, or create a new collection where
* some classes of animal are excluded or have different behaviour.
*/
interface Animals {
void forEach(Consumer<Animal> action);
Animals exclude(Class<Animal> clazz);
Animals configureVoice(Class<Animal> clazz, String voice);
}
/**
* An Animals instance which can contain only a single animal of each class
* (an arbitrary decision based on the code in the question)
*/
class Menagerie implements Animals {
Map<Class<? extends Animal>,Animal> animals = new HashMap<>();
public Menagerie() {
}
public Menagerie(Map<Class<? extends Animal>, Animal> animals) {
this.animals = new HashMap<>(animals);
}
public void add(Animal animal) {
animals.put(animal.getClass(), animal);
}
public void forEach(Consumer<Animal> action) {
animals.values().forEach(action);
}
@Override
public Animals exclude(Class<Animal> clazz) {
Menagerie m = new Menagerie(animals);
m.animals.remove(clazz);
return m;
}
/**
* Return an Animals instance which contains a proxy for a particular type of animal
* which will behave differently when speak() is called.
*/
@Override
public Animals configureVoice(Class<Animal> clazz, String voice) {
Menagerie m = new Menagerie(animals);
Animal a = m.animals.get(clazz);
if (a != null) {
m.animals.put(clazz, new Animal() {
@Override
public String speak() {
return voice;
}
@Override
public String speak(String voice) {
return a.speak(voice);
}
});
}
return m;
}
}
我知道这是用 java
标记的问题。但是,让我举一个 C#
的例子,因为这些语言有很多共同点。
第一件事是我会使用继承并创建抽象 class Animal
as common behaviour Speak()
is used。因此,抽象 class 应该定义行为,派生的 classes 应该实现该行为:
public abstract class Animal
{
public abstract void Speak();
}
然后在派生的 classes:
中使用继承和覆盖行为public class Bird : Animal
{
public override void Speak()
{
System.Console.WriteLine("I am a bird!");
}
}
public class Cat : Animal
{
public override void Speak()
{
System.Console.WriteLine("I am a cat!");
}
}
public class Dog : Animal
{
public override void Speak()
{
System.Console.WriteLine("I am a dog!");
}
}
那么我们需要一个class,让所有的动物都能说话。让我们为此创建 Choir
class:
public class Choir
{
private List<Animal> choristers;
public void AddChoristers(IEnumerable<Animal> choristers)
{
if (choristers == null)
choristers = new List<Animal>();
choristers.AddRange(choristers);
}
public void SpeakAll()
{
foreach (Animal animal in choristers)
animal.Speak();
}
public void Speak(Func<Animal, bool> filter)
{
IEnumerable<Animal> filteredAnimals = choristers
.Where(filter ?? (animal => true));
foreach (Animal animal in filteredAnimals)
animal.Speak();
}
}
注意Speak()
方法。它可以接受一个谓词作为参数,所以你可以选择想要的动物来 speak()
.