如何通过使用超类来减少代码?
How to reduce code by using a superclass?
我想重构一些目前由一个超类和两个子类组成的代码类。
这些是我的 类:
public class Animal {
int a;
int b;
int c;
}
public class Dog extends Animal {
int d;
int e;
}
public class Cat extends Animal {
int f;
int g;
}
这是我当前的代码:
ArrayList<Animal> listAnimal = new ArrayList<>();
if (condition) {
Dog dog = new Dog();
dog.setA(..);
dog.setB(..);
dog.setC(..);
dog.setD(..);
dog.setE(..);
listAnimal.add(dog);
} else {
Cat cat = new Cat();
cat.setA(..);
cat.setB(..);
cat.setC(..);
cat.setF(..);
cat.setG(..);
listAnimal.add(cat);
}
如何重构有关公共属性的代码?
我想要这样的东西:
Animal animal = new Animal();
animal.setA(..);
animal.setB(..);
animal.setC(..);
if (condition) {
Dog anim = (Dog) animal; //I know it doesn't work
anim.setD(..);
anim.setE(..);
} else {
Cat anim = (Cat) animal; //I know it doesn't work
anim.setF(..);
anim.setG(..);
}
listAnimal.add(anim);
您拥有类型 Animal
的变量的想法很好。但是你还必须确保使用正确的构造函数:
Animal animal; // define a variable for whatever animal we will create
if (condition) {
Dog dog = new Dog(); // create a new Dog using the Dog constructor
dog.setD(..);
dog.setE(..);
animal = dog; // let both variables, animal and dog point to the new dog
} else {
Cat cat = new Cat();
cat.setF(..);
cat.setG(..);
animal = cat;
}
animal.setA(..); // modify either cat or dog using the animal methods
animal.setB(..);
animal.setC(..);
listAnimal.add(animal);
提示:如果动物总是猫或狗,请考虑制作动物 abstract
。然后编译器会在您尝试执行 new Animal()
.
时自动报错
构建猫或狗的过程很复杂,因为涉及到很多领域。这是 the builder pattern.
的一个很好的案例
我的想法是为每种类型编写一个构建器并组织它们之间的关系。可以是组合,也可以是继承。
AnimalBuilder
构造一个通用的Animal
对象并管理a
、b
、c
字段
CatBuilder
采用 AnimalBuilder
(或扩展它)并继续构建一个 Cat
对象来管理 f
、g
字段
DogBuilder
采用 AnimalBuilder
(或扩展它)并继续构建一个 Dog
对象来管理 d
、e
字段
如果您不想创建构建器,请考虑为每个子类引入一个具有有意义名称的静态工厂方法:
Animal animal = condition ? Dog.withDE(4, 5) : Cat.withFG(6, 7);
// populate animal's a, b, c
listAnimal.add(animal);
它会简化结构并减少冗长和可读性。
考虑让您的 类 不可变(Effective Java 第三版第 17 项)。如果需要所有参数,请使用构造函数或静态工厂方法(Effective Java 第三版第 1 项)。如果有必需参数和可选参数,请使用构建器模式(有效 Java 第三版第 2 项)。
我会考虑 capabilities/features 的动态 lookup/registration:Flying/Swimming。
问题是这是否适合您的用法:用鸟和鱼代替飞行和游泳。
这取决于添加的属性是独有的 (Dog/Cat) 还是附加的 (Flying/Swimming/Mammal/Insect/EggLaying/...)。后者更适合使用地图进行查找。
interface Fish { boolean inSaltyWater(); }
interface Bird { int wingSpan(); setWingSpan(int span); }
Animal animal = ...
Optional<Fish> fish = animal.as(Fish.class);
fish.ifPresent(f -> System.out.println(f.inSaltyWater()? "seafish" : "riverfish"));
Optional<Bird> bird = animal.as(Bird.class);
bird.ifPresent(b-> b.setWingSpan(6));
Animal
无需实现任何接口,但您可以查找(查找或可能作为)功能。这在未来是可扩展的,动态的:可以在 运行 时间更改。
实现为
private Map<Class<?>, ?> map = new HashMap<>();
public <T> Optional<T> as(Class<T> type) {
return Optional.ofNullable(type.cast(map.get(type)));
}
<S> void register(Class<S> type, S instance) {
map.put(type, instance);
}
该实现进行了安全的动态转换,因为寄存器确保了(键,值)条目的安全填充。
Animal flipper = new Animal();
flipper.register(new Fish() {
@Override
public boolean inSaltyWater() { return true; }
});
回答
一种方法是将适当的构造函数添加到您的 classes。往下看:
public class Animal {
int a, b, c;
public Animal(int a, int b, int c) {
this.a = a;
this.b = b;
this.c = c;
}
}
public class Dog extends Animal {
int d, e;
public Dog(int a, int b, int c, int d, int e) {
super(a, b, c);
this.d = d;
this.e = e;
}
}
public class Cat extends Animal {
int f, g;
public Cat(int a, int b, int c, int f, int g) {
super(a, b, c);
this.f = f;
this.g = g;
}
}
现在,要实例化对象,您可以执行以下操作:
ArrayList<Animal> listAnimal = new ArrayList();
//sample values
int a = 10;
int b = 5;
int c = 20;
if(condition) {
listAnimal.add(new Dog(a, b, c, 9, 11));
//created and added a dog with e = 9 and f = 11
}
else {
listAnimal.add(new Cat(a, b, c, 2, 6));
//created and added a cat with f = 2 and g = 6
}
这是我在这种情况下会使用的方法。通过避免大量的 "set" 方法,它使代码更清晰、更易读。请注意,super()
是对 superclass'(在本例中为 Animal
)构造函数的调用。
奖金
如果您不打算创建 class Animal
的实例,您应该将其声明为 abstract
。 抽象classes 无法实例化,但可以被子classed 并且可以包含[=36=]抽象方法。这些方法声明时没有主体,这意味着所有子 classes 必须提供它们自己的实现。这是一个例子:
public abstract class Animal {
//...
//all animals must eat, but each animal has its own eating behaviour
public abstract void eat();
}
public class Dog {
//...
@Override
public void eat() {
//describe the eating behaviour for dogs
}
}
现在您可以为任何动物打电话 eat()
!在前面的示例中,使用动物列表,您可以执行以下操作:
for(Animal animal: listAnimal) {
animal.eat();
}
作为替代方案,您可以将狗和猫的 "Animal" 部分设为单独的实体,可通过 "Animalian" 界面访问。通过这样做,您首先创建了公共状态,然后在需要时将其提供给物种特定的构造函数。
public class Animal {
int a;
int b;
int c;
}
public interface Animalian {
Animal getAnimal();
}
public class Dog implements Animalian {
int d;
int e;
Animal animal;
public Dog(Animal animal, int d, int e) {
this.animal = animal;
this.d = d;
this.e = e;
}
public Animal getAnimal() {return animal};
}
public class Cat implements Animalian {
int f;
int g;
Animal animal;
public Cat(Animal animal, int f, int g) {
this.animal = animal;
this.f = f;
this.g = g;
}
public Animal getAnimal() {return animal};
}
现在创建动物:
Animal animal = new Animal();
animal.setA(..);
animal.setB(..);
animal.setC(..);
if (condition) {
listAnimalian.add(new Dog(animal, d, e));
} else {
listAnimalian.add(new Cat(animal, f, g));
}
这样做的原因是为了“favor composition over inheritance”。我想表达的是,这只是解决所提出问题的另一种方式。这并不意味着组合在任何时候都应该比继承更受青睐。由工程师根据出现问题的上下文确定正确的解决方案。
有 很多 个 reading on this topic。
这是一个解决方案,与 slartidan 的解决方案非常接近,但是使用 setter
和构建器的风格,避免了 dog
和 cat
变量
public class Dog extends Animal
{
// stuff
Dog setD(...)
{
//...
return this;
}
Dog setE(...)
{
//...
return this;
}
}
public class Cat extends Animal
{
// stuff
Cat setF(...)
{
//...
return this;
}
Cat setG(...)
{
//...
return this;
}
}
Animal animal = condition ?
new Dog().setD(..).setE(..) :
new Cat().setF(..).setG(..);
animal.setA(..);
animal.setB(..);
animal.setC(..);
listAnimal.add(animal);
以下是我想提出的建议:
import java.util.ArrayList;
import java.util.List;
class Animal {
int a;
int b;
int c;
public Animal setA(int a) {
this.a = a;
return this;
}
public Animal setB(int b) {
this.b = b;
return this;
}
public Animal setC(int c) {
this.c = c;
return this;
}
}
class Dog extends Animal {
int d;
int e;
public Dog setD(int d) {
this.d = d;
return this;
}
public Dog setE(int e) {
this.e = e;
return this;
}
}
class Cat extends Animal {
int f;
int g;
public Cat setF(int f) {
this.f = f;
return this;
}
public Cat setG(int g) {
this.g = g;
return this;
}
}
class Scratch {
public static void main(String[] args) {
List<Animal> listAnimal = new ArrayList();
boolean condition = true;
Animal animal;
if (condition) {
animal = new Dog()
.setD(4)
.setE(5);
} else {
animal = new Cat()
.setF(14)
.setG(15);
}
animal.setA(1)
.setB(2)
.setC(3);
listAnimal.add(animal);
System.out.println(listAnimal);
}
}
一些值得注意的点:
- 在声明中使用 List 接口
List<Animal> listAnimal
- 创建对象时使用界面动物
Animal animal;
abstract
class 动物
- Setter 返回
this
以使代码更清晰。或者你必须使用像 animal.setD(4);
animal.setE(5);
这样的代码
这样我们就可以利用接口Animal,一次性设置公共属性。希望这会有所帮助。
将您的代码重构为:
ArrayList<Animal> listAnimal = new ArrayList();
//Other code...
if (animalIsDog) {
addDogTo(listAnimal, commonAttribute, dogSpecificAttribute);
} else {
addCatTo(listAnimal, commonAttribute, catSpecificAttribute);
}
新代码的好处:
- 隐藏的复杂性:您已经隐藏了复杂性,您现在必须查看更小的代码,几乎用简单的英语编写,稍后重新访问您的代码时。
但是现在,方法addDogTo
和addCatTo
就得写了。这就是它们的样子:
private void addDogTo(ArrayList<Animal> listAnimal,
AnimalAttribute generalAttribute,
DogAttribute specificAttribute) {
var dog = createDog(commonAttribute, specificAttribute);
listAnimal.add(dog);
}
private void addCatTo(ArrayList<Animal> listAnimal,
AnimalAttribute generalAttribute,
CatAttribute specificAttribute) {
var cat = createCat(commonAttribute, specificAttribute);
listAnimal.add(cat);
}
好处:
- 隐藏的复杂性;
- 两种方法都是私有的:这意味着它们只能从class内部调用。所以你可以安全地取消检查输入是否为 null 等,因为调用者(在 class 内)必须注意不要将虚假数据传递给它自己的成员。
这意味着现在我们需要有 createDog
和 createCat
方法。这就是我编写这些方法的方式:
private Dog createDog(AnimalAttribute generalAttribute,
DogAttribute specificAttribute) {
var dog = new Dog(generalAttribute, specificAttribute);
return dog;
}
private Cat createCat(AnimalAttribute generalAttribute,
CatAttribute specificAttribute) {
var cat = new Cat(generalAttribute, specificAttribute);
return cat;
}
好处:
- 隐藏的复杂性;
- 两种方法都是私有的.
现在,对于上面编写的代码,您将不得不为 Cat
和 Dog
编写构造函数,这些构造函数接受对象构造的公共属性和特定属性。这看起来像:
public Dog(AnimalAttribute generalAttribute,
DogAttribute specificAttribute)
: base (generalAttribute) {
this.d = specificAttribute.getD();
this.e = specificAttribute.getE();
}
并且,
public Cat(AnimalAttribute generalAttribute,
CatAttribute specificAttribute)
: base (generalAttribute) {
this.f = specificAttribute.getF();
this.g = specificAttribute.getG();
}
好处:
- 代码是 DRY: 两个构造函数都使用
generalAttributes
调用 superclass 方法,它处理两个 sub-[=119 的共同属性=] 对象;
- 整个对象被保留: 不是调用构造函数并向它传递 20,000 个参数,而是传递 2 个参数,即。一般动物属性对象和特定动物属性对象。这两个参数包含其中的其余属性,并在需要时在构造函数中取消装箱。
最后,您的 Animal
的构造函数将如下所示:
public Animal(AnimalAttribute attribute) {
this.a = attribute.getA();
this.b = attribute.getB();
this.c = attribute.getC();
}
好处:
- 整个对象被保留;
为了完成:
AnimalAttribute
/DogAttribute
/CatAttribute
classes 只有一些字段和这些字段的 getter 和 setter;
- 这些字段是构造
Animal
/Dog
/Cat
对象所需的数据。
这里有很多很棒的建议。我会使用我个人最喜欢的构建器模式(但添加了继承风格):
public class Animal {
int a;
int b;
int c;
public Animal() {
}
private <T> Animal(Builder<T> builder) {
this.a = builder.a;
this.b = builder.b;
this.c = builder.c;
}
public static class Builder<T> {
Class<T> builderClass;
int a;
int b;
int c;
public Builder(Class<T> builderClass) {
this.builderClass = builderClass;
}
public T a(int a) {
this.a = a;
return builderClass.cast(this);
}
public T b(int b) {
this.b = b;
return builderClass.cast(this);
}
public T c(int c) {
this.c = c;
return builderClass.cast(this);
}
public Animal build() {
return new Animal(this);
}
}
// getters and setters goes here
}
public class Dog extends Animal {
int d;
int e;
private Dog(DogBuilder builder) {
this.d = builder.d;
this.e = builder.e;
}
public static class DogBuilder extends Builder<DogBuilder> {
int d;
int e;
public DogBuilder() {
super(DogBuilder.class);
}
public DogBuilder d(int d) {
this.d = d;
return this;
}
public DogBuilder e(int e) {
this.e = e;
return this;
}
public Dog build() {
return new Dog(this);
}
}
// getters and setters goes here
}
public class Cat extends Animal {
int f;
int g;
private Cat(CatBuilder builder) {
this.f = builder.f;
this.g = builder.g;
}
public static class CatBuilder extends Builder<CatBuilder> {
int f;
int g;
public CatBuilder() {
super(CatBuilder.class);
}
public CatBuilder f(int f) {
this.f = f;
return this;
}
public CatBuilder g(int g) {
this.g = g;
return this;
}
public Cat build() {
return new Cat(this);
}
}
// getters and setters goes here
}
public class TestDrive {
public static void main(String[] args) {
Boolean condition = true;
ArrayList<Animal> listAnimal = new ArrayList<>();
if (condition) {
Dog dogA = new Dog.DogBuilder().a(1).b(2).c(3).d(4).e(5).build();
Dog dogB = new Dog.DogBuilder().d(4).build();
listAnimal.add(dogA);
listAnimal.add(dogB);
} else {
Cat catA = new Cat.CatBuilder().b(2).f(6).g(7).build();
Cat catB = new Cat.CatBuilder().g(7).build();
listAnimal.add(catA);
listAnimal.add(catB);
}
Dog doggo = (Dog) listAnimal.get(0);
System.out.println(doggo.d);
}
}
注意: Animal.Builder
构造函数将 Class builderClass
作为通用参数。当 return.
时将对象的当前实例转换为此 class
我想重构一些目前由一个超类和两个子类组成的代码类。
这些是我的 类:
public class Animal {
int a;
int b;
int c;
}
public class Dog extends Animal {
int d;
int e;
}
public class Cat extends Animal {
int f;
int g;
}
这是我当前的代码:
ArrayList<Animal> listAnimal = new ArrayList<>();
if (condition) {
Dog dog = new Dog();
dog.setA(..);
dog.setB(..);
dog.setC(..);
dog.setD(..);
dog.setE(..);
listAnimal.add(dog);
} else {
Cat cat = new Cat();
cat.setA(..);
cat.setB(..);
cat.setC(..);
cat.setF(..);
cat.setG(..);
listAnimal.add(cat);
}
如何重构有关公共属性的代码?
我想要这样的东西:
Animal animal = new Animal();
animal.setA(..);
animal.setB(..);
animal.setC(..);
if (condition) {
Dog anim = (Dog) animal; //I know it doesn't work
anim.setD(..);
anim.setE(..);
} else {
Cat anim = (Cat) animal; //I know it doesn't work
anim.setF(..);
anim.setG(..);
}
listAnimal.add(anim);
您拥有类型 Animal
的变量的想法很好。但是你还必须确保使用正确的构造函数:
Animal animal; // define a variable for whatever animal we will create
if (condition) {
Dog dog = new Dog(); // create a new Dog using the Dog constructor
dog.setD(..);
dog.setE(..);
animal = dog; // let both variables, animal and dog point to the new dog
} else {
Cat cat = new Cat();
cat.setF(..);
cat.setG(..);
animal = cat;
}
animal.setA(..); // modify either cat or dog using the animal methods
animal.setB(..);
animal.setC(..);
listAnimal.add(animal);
提示:如果动物总是猫或狗,请考虑制作动物 abstract
。然后编译器会在您尝试执行 new Animal()
.
构建猫或狗的过程很复杂,因为涉及到很多领域。这是 the builder pattern.
的一个很好的案例我的想法是为每种类型编写一个构建器并组织它们之间的关系。可以是组合,也可以是继承。
AnimalBuilder
构造一个通用的Animal
对象并管理a
、b
、c
字段CatBuilder
采用AnimalBuilder
(或扩展它)并继续构建一个Cat
对象来管理f
、g
字段DogBuilder
采用AnimalBuilder
(或扩展它)并继续构建一个Dog
对象来管理d
、e
字段
如果您不想创建构建器,请考虑为每个子类引入一个具有有意义名称的静态工厂方法:
Animal animal = condition ? Dog.withDE(4, 5) : Cat.withFG(6, 7);
// populate animal's a, b, c
listAnimal.add(animal);
它会简化结构并减少冗长和可读性。
考虑让您的 类 不可变(Effective Java 第三版第 17 项)。如果需要所有参数,请使用构造函数或静态工厂方法(Effective Java 第三版第 1 项)。如果有必需参数和可选参数,请使用构建器模式(有效 Java 第三版第 2 项)。
我会考虑 capabilities/features 的动态 lookup/registration:Flying/Swimming。
问题是这是否适合您的用法:用鸟和鱼代替飞行和游泳。
这取决于添加的属性是独有的 (Dog/Cat) 还是附加的 (Flying/Swimming/Mammal/Insect/EggLaying/...)。后者更适合使用地图进行查找。
interface Fish { boolean inSaltyWater(); }
interface Bird { int wingSpan(); setWingSpan(int span); }
Animal animal = ...
Optional<Fish> fish = animal.as(Fish.class);
fish.ifPresent(f -> System.out.println(f.inSaltyWater()? "seafish" : "riverfish"));
Optional<Bird> bird = animal.as(Bird.class);
bird.ifPresent(b-> b.setWingSpan(6));
Animal
无需实现任何接口,但您可以查找(查找或可能作为)功能。这在未来是可扩展的,动态的:可以在 运行 时间更改。
实现为
private Map<Class<?>, ?> map = new HashMap<>();
public <T> Optional<T> as(Class<T> type) {
return Optional.ofNullable(type.cast(map.get(type)));
}
<S> void register(Class<S> type, S instance) {
map.put(type, instance);
}
该实现进行了安全的动态转换,因为寄存器确保了(键,值)条目的安全填充。
Animal flipper = new Animal();
flipper.register(new Fish() {
@Override
public boolean inSaltyWater() { return true; }
});
回答
一种方法是将适当的构造函数添加到您的 classes。往下看:
public class Animal {
int a, b, c;
public Animal(int a, int b, int c) {
this.a = a;
this.b = b;
this.c = c;
}
}
public class Dog extends Animal {
int d, e;
public Dog(int a, int b, int c, int d, int e) {
super(a, b, c);
this.d = d;
this.e = e;
}
}
public class Cat extends Animal {
int f, g;
public Cat(int a, int b, int c, int f, int g) {
super(a, b, c);
this.f = f;
this.g = g;
}
}
现在,要实例化对象,您可以执行以下操作:
ArrayList<Animal> listAnimal = new ArrayList();
//sample values
int a = 10;
int b = 5;
int c = 20;
if(condition) {
listAnimal.add(new Dog(a, b, c, 9, 11));
//created and added a dog with e = 9 and f = 11
}
else {
listAnimal.add(new Cat(a, b, c, 2, 6));
//created and added a cat with f = 2 and g = 6
}
这是我在这种情况下会使用的方法。通过避免大量的 "set" 方法,它使代码更清晰、更易读。请注意,super()
是对 superclass'(在本例中为 Animal
)构造函数的调用。
奖金
如果您不打算创建 class Animal
的实例,您应该将其声明为 abstract
。 抽象classes 无法实例化,但可以被子classed 并且可以包含[=36=]抽象方法。这些方法声明时没有主体,这意味着所有子 classes 必须提供它们自己的实现。这是一个例子:
public abstract class Animal {
//...
//all animals must eat, but each animal has its own eating behaviour
public abstract void eat();
}
public class Dog {
//...
@Override
public void eat() {
//describe the eating behaviour for dogs
}
}
现在您可以为任何动物打电话 eat()
!在前面的示例中,使用动物列表,您可以执行以下操作:
for(Animal animal: listAnimal) {
animal.eat();
}
作为替代方案,您可以将狗和猫的 "Animal" 部分设为单独的实体,可通过 "Animalian" 界面访问。通过这样做,您首先创建了公共状态,然后在需要时将其提供给物种特定的构造函数。
public class Animal {
int a;
int b;
int c;
}
public interface Animalian {
Animal getAnimal();
}
public class Dog implements Animalian {
int d;
int e;
Animal animal;
public Dog(Animal animal, int d, int e) {
this.animal = animal;
this.d = d;
this.e = e;
}
public Animal getAnimal() {return animal};
}
public class Cat implements Animalian {
int f;
int g;
Animal animal;
public Cat(Animal animal, int f, int g) {
this.animal = animal;
this.f = f;
this.g = g;
}
public Animal getAnimal() {return animal};
}
现在创建动物:
Animal animal = new Animal();
animal.setA(..);
animal.setB(..);
animal.setC(..);
if (condition) {
listAnimalian.add(new Dog(animal, d, e));
} else {
listAnimalian.add(new Cat(animal, f, g));
}
这样做的原因是为了“favor composition over inheritance”。我想表达的是,这只是解决所提出问题的另一种方式。这并不意味着组合在任何时候都应该比继承更受青睐。由工程师根据出现问题的上下文确定正确的解决方案。
有 很多 个 reading on this topic。
这是一个解决方案,与 slartidan 的解决方案非常接近,但是使用 setter
和构建器的风格,避免了 dog
和 cat
变量
public class Dog extends Animal
{
// stuff
Dog setD(...)
{
//...
return this;
}
Dog setE(...)
{
//...
return this;
}
}
public class Cat extends Animal
{
// stuff
Cat setF(...)
{
//...
return this;
}
Cat setG(...)
{
//...
return this;
}
}
Animal animal = condition ?
new Dog().setD(..).setE(..) :
new Cat().setF(..).setG(..);
animal.setA(..);
animal.setB(..);
animal.setC(..);
listAnimal.add(animal);
以下是我想提出的建议:
import java.util.ArrayList;
import java.util.List;
class Animal {
int a;
int b;
int c;
public Animal setA(int a) {
this.a = a;
return this;
}
public Animal setB(int b) {
this.b = b;
return this;
}
public Animal setC(int c) {
this.c = c;
return this;
}
}
class Dog extends Animal {
int d;
int e;
public Dog setD(int d) {
this.d = d;
return this;
}
public Dog setE(int e) {
this.e = e;
return this;
}
}
class Cat extends Animal {
int f;
int g;
public Cat setF(int f) {
this.f = f;
return this;
}
public Cat setG(int g) {
this.g = g;
return this;
}
}
class Scratch {
public static void main(String[] args) {
List<Animal> listAnimal = new ArrayList();
boolean condition = true;
Animal animal;
if (condition) {
animal = new Dog()
.setD(4)
.setE(5);
} else {
animal = new Cat()
.setF(14)
.setG(15);
}
animal.setA(1)
.setB(2)
.setC(3);
listAnimal.add(animal);
System.out.println(listAnimal);
}
}
一些值得注意的点:
- 在声明中使用 List 接口
List<Animal> listAnimal
- 创建对象时使用界面动物
Animal animal;
abstract
class 动物- Setter 返回
this
以使代码更清晰。或者你必须使用像animal.setD(4);
animal.setE(5);
这样的代码
这样我们就可以利用接口Animal,一次性设置公共属性。希望这会有所帮助。
将您的代码重构为:
ArrayList<Animal> listAnimal = new ArrayList();
//Other code...
if (animalIsDog) {
addDogTo(listAnimal, commonAttribute, dogSpecificAttribute);
} else {
addCatTo(listAnimal, commonAttribute, catSpecificAttribute);
}
新代码的好处:
- 隐藏的复杂性:您已经隐藏了复杂性,您现在必须查看更小的代码,几乎用简单的英语编写,稍后重新访问您的代码时。
但是现在,方法addDogTo
和addCatTo
就得写了。这就是它们的样子:
private void addDogTo(ArrayList<Animal> listAnimal,
AnimalAttribute generalAttribute,
DogAttribute specificAttribute) {
var dog = createDog(commonAttribute, specificAttribute);
listAnimal.add(dog);
}
private void addCatTo(ArrayList<Animal> listAnimal,
AnimalAttribute generalAttribute,
CatAttribute specificAttribute) {
var cat = createCat(commonAttribute, specificAttribute);
listAnimal.add(cat);
}
好处:
- 隐藏的复杂性;
- 两种方法都是私有的:这意味着它们只能从class内部调用。所以你可以安全地取消检查输入是否为 null 等,因为调用者(在 class 内)必须注意不要将虚假数据传递给它自己的成员。
这意味着现在我们需要有 createDog
和 createCat
方法。这就是我编写这些方法的方式:
private Dog createDog(AnimalAttribute generalAttribute,
DogAttribute specificAttribute) {
var dog = new Dog(generalAttribute, specificAttribute);
return dog;
}
private Cat createCat(AnimalAttribute generalAttribute,
CatAttribute specificAttribute) {
var cat = new Cat(generalAttribute, specificAttribute);
return cat;
}
好处:
- 隐藏的复杂性;
- 两种方法都是私有的.
现在,对于上面编写的代码,您将不得不为 Cat
和 Dog
编写构造函数,这些构造函数接受对象构造的公共属性和特定属性。这看起来像:
public Dog(AnimalAttribute generalAttribute,
DogAttribute specificAttribute)
: base (generalAttribute) {
this.d = specificAttribute.getD();
this.e = specificAttribute.getE();
}
并且,
public Cat(AnimalAttribute generalAttribute,
CatAttribute specificAttribute)
: base (generalAttribute) {
this.f = specificAttribute.getF();
this.g = specificAttribute.getG();
}
好处:
- 代码是 DRY: 两个构造函数都使用
generalAttributes
调用 superclass 方法,它处理两个 sub-[=119 的共同属性=] 对象; - 整个对象被保留: 不是调用构造函数并向它传递 20,000 个参数,而是传递 2 个参数,即。一般动物属性对象和特定动物属性对象。这两个参数包含其中的其余属性,并在需要时在构造函数中取消装箱。
最后,您的 Animal
的构造函数将如下所示:
public Animal(AnimalAttribute attribute) {
this.a = attribute.getA();
this.b = attribute.getB();
this.c = attribute.getC();
}
好处:
- 整个对象被保留;
为了完成:
AnimalAttribute
/DogAttribute
/CatAttribute
classes 只有一些字段和这些字段的 getter 和 setter;- 这些字段是构造
Animal
/Dog
/Cat
对象所需的数据。
这里有很多很棒的建议。我会使用我个人最喜欢的构建器模式(但添加了继承风格):
public class Animal {
int a;
int b;
int c;
public Animal() {
}
private <T> Animal(Builder<T> builder) {
this.a = builder.a;
this.b = builder.b;
this.c = builder.c;
}
public static class Builder<T> {
Class<T> builderClass;
int a;
int b;
int c;
public Builder(Class<T> builderClass) {
this.builderClass = builderClass;
}
public T a(int a) {
this.a = a;
return builderClass.cast(this);
}
public T b(int b) {
this.b = b;
return builderClass.cast(this);
}
public T c(int c) {
this.c = c;
return builderClass.cast(this);
}
public Animal build() {
return new Animal(this);
}
}
// getters and setters goes here
}
public class Dog extends Animal {
int d;
int e;
private Dog(DogBuilder builder) {
this.d = builder.d;
this.e = builder.e;
}
public static class DogBuilder extends Builder<DogBuilder> {
int d;
int e;
public DogBuilder() {
super(DogBuilder.class);
}
public DogBuilder d(int d) {
this.d = d;
return this;
}
public DogBuilder e(int e) {
this.e = e;
return this;
}
public Dog build() {
return new Dog(this);
}
}
// getters and setters goes here
}
public class Cat extends Animal {
int f;
int g;
private Cat(CatBuilder builder) {
this.f = builder.f;
this.g = builder.g;
}
public static class CatBuilder extends Builder<CatBuilder> {
int f;
int g;
public CatBuilder() {
super(CatBuilder.class);
}
public CatBuilder f(int f) {
this.f = f;
return this;
}
public CatBuilder g(int g) {
this.g = g;
return this;
}
public Cat build() {
return new Cat(this);
}
}
// getters and setters goes here
}
public class TestDrive {
public static void main(String[] args) {
Boolean condition = true;
ArrayList<Animal> listAnimal = new ArrayList<>();
if (condition) {
Dog dogA = new Dog.DogBuilder().a(1).b(2).c(3).d(4).e(5).build();
Dog dogB = new Dog.DogBuilder().d(4).build();
listAnimal.add(dogA);
listAnimal.add(dogB);
} else {
Cat catA = new Cat.CatBuilder().b(2).f(6).g(7).build();
Cat catB = new Cat.CatBuilder().g(7).build();
listAnimal.add(catA);
listAnimal.add(catB);
}
Dog doggo = (Dog) listAnimal.get(0);
System.out.println(doggo.d);
}
}
注意: Animal.Builder
构造函数将 Class builderClass
作为通用参数。当 return.