How to reduce code by using a superclass?
我想重构一些目前由超类和两个子类组成的代码。
这些是我的课程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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; } |
这是我目前的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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); } |
如何重构有关公共属性的代码?
我想要这样的东西:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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); |
你想要一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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); |
提示:如果动物总是猫或狗,请考虑制作动物
构建猫或狗的过程很复杂,因为涉及很多领域。对于构建器模式来说,这是一个很好的例子。
我的想法是为每种类型编写一个构建器并组织它们之间的关系。它可以是组合或继承。
-
AnimalBuilder 构造一般的Animal 对象并管理a ,b ,c 字段 -
CatBuilder 获取AnimalBuilder (或扩展它)并继续构建管理f ,g 字段的Cat 对象 -
DogBuilder 获取AnimalBuilder (或扩展它)并继续构建管理d ,e 字段的Dog 对象
如果您不想创建构建器,请考虑为每个子类引入一个具有有意义名称的静态工厂方法:
1 2 3 | Animal animal = condition ? Dog.withDE(4, 5) : Cat.withFG(6, 7); // populate animal's a, b, c listAnimal.add(animal); |
它将简化构造并使其更简洁,更易读。
回答
一种方法是在类中添加适当的构造函数。往下看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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; } } |
现在,要实例化对象,您可以执行以下操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 } |
这是我在这种情况下使用的方法。它通过避免大量的"设置"方法使代码更清晰,更可读。请注意,
奖金
如果您不打算创建类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 } } |
现在你可以为任何动物打电话
1 2 3 | for(Animal animal: listAnimal) { animal.eat(); } |
以下是我想提出的建议:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | 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); } } |
一些值得注意的要点:
这样我们就可以使用Animal接口并设置一次公共属性。希望这可以帮助。
这是一个解决方案,非常接近slartidan的一个,但使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 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); |
作为替代方案,您可以将您的狗和猫的"动物"部分设置为通过"动物"界面提供的单独实体。通过这样做,您首先创建公共状态,然后在需要时将其提供给特定于物种的构造函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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}; } |
现在创造动物:
1 2 3 4 5 6 7 8 9 10 | 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)); } |
这样做的原因是"赞成合成而非继承"。我想表达,这只是解决问题的另一种方式。这并不意味着组合在任何时候都应该优于继承。由工程师决定出现问题的背景的正确解决方案。
关于这个主题有很多阅读。
我会考虑动态查找/注册功能/功能:飞行/游泳。
这是否适合您的使用问题:而不是飞行&amp;游泳带鸟和鱼。
这取决于添加的属性是否是独家(狗/猫)或添加剂(飞行/游泳/哺乳动物/昆虫/ EggLaying / ...)。后者更适合使用地图进行查找。
1 2 3 4 5 6 7 8 9 10 | 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)); |
实施为
1 2 3 4 5 6 7 8 9 | private Map<Class< ? >, ?> map = new HashMap<>(); public < T > Optional< T > as(Class< T > type) { return Optional.ofNullable(type.cast(map.get(type))); } void register(Class type, S instance) { map.put(type, instance); } |
该实现执行安全的动态转换,因为寄存器确保安全填充(键,值)条目。
1 2 3 4 5 | Animal flipper = new Animal(); flipper.register(new Fish() { @Override public boolean inSaltyWater() { return true; } }); |
考虑使您的类不可变(Effective Java 3rd Edition Item 17)。如果需要所有参数,请使用构造函数或静态工厂方法(Effective Java 3rd Edition Item 1)。如果有必需参数和可选参数,请使用构建器模式(Effective Java 3rd Edition Item 2)。
将您的代码重构为:
1 2 3 4 5 6 7 8 9 | ArrayList<Animal> listAnimal = new ArrayList(); //Other code... if (animalIsDog) { addDogTo(listAnimal, commonAttribute, dogSpecificAttribute); } else { addCatTo(listAnimal, commonAttribute, catSpecificAttribute); } |
新代码的好处:
但是现在,必须编写方法
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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); } |
优点:
这意味着现在我们需要有
1 2 3 4 5 6 7 8 9 10 11 | 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; } |
优点:
现在,对于上面编写的代码,您将必须编写
1 2 3 4 5 6 | public Dog(AnimalAttribute generalAttribute, DogAttribute specificAttribute) : base (generalAttribute) { this.d = specificAttribute.getD(); this.e = specificAttribute.getE(); } |
和,
1 2 3 4 5 6 | public Cat(AnimalAttribute generalAttribute, CatAttribute specificAttribute) : base (generalAttribute) { this.f = specificAttribute.getF(); this.g = specificAttribute.getG(); } |
优点:
最后,你的
1 2 3 4 5 | public Animal(AnimalAttribute attribute) { this.a = attribute.getA(); this.b = attribute.getB(); this.c = attribute.getC(); } |
优点:
为了完成:
-
AnimalAttribute /DogAttribute /CatAttribute 类只有一些字段以及这些字段的getter和setter; -
这些字段是构造
Animal /Dog /Cat 对象所需的数据。
这里有很多好建议。我会使用我个人喜欢的构建器模式(但添加了继承风味):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | 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); } } |
注意: