一、前言
最近的接收的一套代码,真是吐血,我在代码审查的时候发现系统代码一大堆问题,阅读起来非常艰涩难懂,主要是系统代码中使用了大量得到继承,什么都是继承。而且是嵌套多层。这种设计真的很垃圾。继承在代码中本来是好事,但是用的多了就是坏事了,特别是嵌套层次多的继承。
二、java继承
Java中的继承是一种面向对象编程(OOP)的重要特性,它允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法。通过继承,子类可以重用父类的代码,并且可以添加或覆盖父类的属性和方法,以实现更具体的功能。
继承的基本语法
在Java中,使用extends
关键字来实现继承。下面是一个简单的例子:
class Animal {
void eat() {
System.out.println("The animal eats");
}
}
class Dog extends Animal {
void bark() {
System.out.println("The dog barks");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat(); // 继承自Animal的方法
dog.bark(); // Dog类自己的方法
}
}
在这个例子中,Dog
类继承了Animal
类。因此,Dog
类的对象可以调用eat()
方法,这个方法是从Animal
类继承来的。同时,Dog
类还定义了自己的bark()
方法。
继承的特点
-
代码重用:子类可以继承父类的属性和方法,避免了在子类中重复编写相同的代码。
-
扩展性:子类可以在继承父类的基础上添加新的属性和方法,或者覆盖父类的方法以实现不同的行为。
-
多态性:通过继承,子类对象可以被视为父类对象处理,这是多态性的基础。
继承的层次结构
Java中的继承是单一继承的,即一个类只能直接继承自一个父类。但是,一个类可以间接地继承自多个类,通过继承链实现。例如,如果类B继承自类A,类C又继承自类B,那么类C间接地继承自类A。
访问修饰符与继承
访问修饰符(如public
、protected
、default
(无修饰符)和private
)决定了类的成员(属性和方法)的可访问性。在继承中,不同访问修饰符的成员具有不同的可继承性:
public
:子类可以访问父类的public
成员。protected
:子类、同一个包中的其他类以及任何其他类的子类都可以访问protected
成员。default
(无修饰符):子类以及同一个包中的其他类可以访问无修饰符的成员。private
:private
成员只能被其所在的类访问,不能被子类访问。
重写(Overriding)
子类可以提供一个与父类方法签名相同但实现不同的方法,这称为方法重写。当通过子类对象调用这个方法时,会执行子类中的方法实现,而不是父类中的。重写方法时,访问修饰符的可见性不能低于父类中被重写方法的可见性。
构造方法与继承
子类不能继承父类的构造方法,但子类构造方法可以通过super
关键字调用父类的构造方法。如果子类构造方法没有显式地调用父类的构造方法,那么它会自动调用父类的无参数构造方法。如果父类没有定义无参数的构造方法,且子类构造方法中没有通过super
显式调用父类的其他构造方法,则编译器会报错。
通过继承,Java代码可以更加模块化、可维护,并且提高了代码的重用性。然而,过度使用继承也可能导致代码结构变得复杂和难以管理,因此在实际编程中需要谨慎使用。
三、看看垃圾代码
/**
* 获取商品可用店铺列表
*/
@ActionConstructInit(parameterVoName = "requestProductDetailModel")
public List<SysStore> getStoreList() {
RequestProductDetailModel requestProductDetailModel = getRequestProductDetailModel();
Long skuId = requestProductDetailModel.getSkuId();
ProSku sku = new ProSku();
sku.setId(skuId);
sku.load();
if (sku.getIsLoad()) {
if (StringUtils.isNotBlank(sku.getApplyStoreCodes())) {
List<String> storeCodeList = Arrays.asList(sku.getApplyStoreCodes().split(","));
SysStore sysStore = new SysStore();
sysStore.addConditions("inStoreCode", storeCodeList);
return sysStore.select();
} else {
return StoreUtil.queryOfflineStoreByMchOuCode(sku.getMchOuCode());
}
}
return new ArrayList<>();
}
子子孙孙无穷无尽也。
一个业务类里写上了 8000千 一万的的代码,也是服了
四、java组合
在Java中,"组合"(Composition)是一种实现代码复用和构建复杂对象结构的重要技术。它允许一个类的对象包含另一个类的对象作为自己的属性,这样新对象就可以继承被包含对象的行为和状态。与继承相比,组合是一种更加灵活且松耦合的方式来扩展对象的功能。
组合的一个关键优势在于它允许你构建更加灵活和可维护的代码。通过使用组合,你可以将对象组合成更复杂的对象,而这些对象可以独立地变化,而不会影响其他对象。
下面是一个简单的Java组合示例:
// 被组合的对象
class Engine {
void start() {
System.out.println("Engine started");
}
}
// 组合了Engine对象的对象
class Car {
private Engine engine; // 组合了Engine对象
public Car() {
this.engine = new Engine(); // 初始化Engine对象
}
void startCar() {
engine.start(); // 调用Engine对象的start方法
System.out.println("Car started");
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car();
car.startCar(); // 输出:Engine started 和 Car started
}
}
在这个例子中,Car
类组合了一个Engine
对象。当调用Car
对象的startCar
方法时,它会首先调用Engine
对象的start
方法,然后再执行自己的逻辑。
组合与继承的主要区别在于:
- 继承:子类继承父类的属性和方法,子类可以重写父类的方法或添加新的方法。继承是"is-a"关系,例如,
Dog
是Animal
的一种。 - 组合:一个类的对象包含另一个类的对象作为自己的属性。组合是"has-a"关系,例如,
Car
有一个Engine
。
组合通常比继承更加灵活,因为它允许你动态地改变被组合对象的类型,而继承则是一种静态的关系,一旦确定子类与父类的关系,就不能轻易改变。此外,过度使用继承可能导致类层次结构变得复杂和难以维护,而组合则可以通过将对象组合成更小的、更易于管理的部分来避免这个问题。
五、java为什么要多用组合少用继承
在Java中,推荐多用组合少用继承的原因主要有以下几点:
- 避免多继承的局限性:Java不支持多继承,这意味着一个类只能继承自一个父类。因此,如果希望赋予一个类多个功能,组合(通过接口和成员变量)是一个更灵活的选择。
- 提高测试的可维护性:在单元测试中,使用组合可以使mock数据更加容易。当使用继承时,可能需要mock整个基类,而使用组合则可以通过注入不同的实例来方便地完成mock和线上实例的切换。
- 满足设计模式的需要:现有成熟的设计模式,如策略模式(Strategy design pattern)和装饰者模式(Decorator design pattern),都倾向于使用组合的形式。这些模式通过组合不同的对象和行为来实现灵活性和可扩展性。
- 减少封装问题:继承中,子类对父类的依赖可能导致子类变得脆弱。一旦父类行为发生变化(如代码结构或性能优化等原因),子类也可能受到影响。而组合通过显式地包含所需的对象和功能,减少了这种依赖性问题。
- 实现更灵活的代码复用:通过聚合/组合关系,可以避免继承带来的方法污染问题,具有很强的代码重用性和灵活性。聚合/组合复用也可以在运行时动态进行,这使得组合能够更灵活地适应不同的需求和场景。
- 解决复杂的继承关系:当继承层次过深或过复杂时,会影响代码的可维护性。在这种情况下,使用组合可以更有效地管理类的结构和关系,减少继承带来的复杂性。
总的来说,虽然继承是面向对象编程的一个重要特性,但在Java中,由于多继承的限制和上述优势,推荐使用组合来扩展类的功能。当然,在实际的项目开发中,我们还需要根据具体的情况和需求来选择使用继承还是组合。