抽象类的使用
- 一、设计模式中的使用
- 1. 模板方法模式
- 2. 抽象工厂模式
- 二、扩展性和灵活性
- 三、为什么开发时抽象类用的很少,反而用接口的很多
一、设计模式中的使用
1. 模板方法模式
1.定义:当你需要为一组相关类提供一个操作的框架,其中一些步骤是固定的,而其他步骤(由抽象方法表示)则在子类中实现。例如,制定一个学习计划,其中学习计划的基本流程是固定的(比如先预习、再上课、最后复习),但是每一步的具体实现可能因学生类型而异。
2.结构:
- 抽象类:定义并实现一个模板方法,该方法定义了算法的骨架,并调用一些抽象方法,这些抽象方法由子类实现。
- 具体子类:实现抽象类中声明的抽象方法,完成具体的步骤。
3.代码
定义一个抽象类
/*** 抽象类:学习计划模板*/
public abstract class StudyPlan {// 模板方法,定义学习流程的骨架public final void studyProcess(String name) {preview(name);attendClass(name);review(name);}// 预习protected abstract void preview(String name);// 上课protected abstract void attendClass(String name);// 复习protected abstract void review(String name);
}
中学生学习计划子类的实现
/*** 中学生学习计划*/
public class MiddleStudentStudyPlan extends StudyPlan {@Overrideprotected void preview(String name) {System.out.println(name + ":预习");}@Overrideprotected void attendClass(String name) {System.out.println(name + ":上课");}@Overrideprotected void review(String name) {System.out.println(name + ":复习");}
}
小学生学习计划子类的实现
/*** 小学生学习计划*/
public class PrimaryStudentStudyPlan extends StudyPlan {@Overrideprotected void preview(String name) {System.out.println(name + ":预习");}@Overrideprotected void attendClass(String name) {System.out.println(name + ":上课");}@Overrideprotected void review(String name) {System.out.println(name + ":复习");}}
测试
public class TestMoBan {public static void main(String[] args) {// 小学生的学习计划new PrimaryStudentStudyPlan().studyProcess("小学生");// 中学生的学习计划new MiddleStudentStudyPlan().studyProcess("中学生");}
}
2. 抽象工厂模式
1.什么是抽象工厂模式 : 它提供了一种方式来创建一系列相关的或相互依赖的对象,而无需指定它们具体的类。这种模式允许客户端使用抽象接口来操作对象,而不需要了解具体类的实现细节,从而使得系统在不修改具体类的情况下引入新产品族变得容易。
2.结构:
- 抽象工厂:声明一个创建一系列相关或相互依赖对象的接口,而不需要指定它们具体的类。
- 具体工厂:实现抽象工厂接口,负责创建一个产品系列的具体对象。
- 抽象产品:定义一组产品的接口,使得在各个具体产品之间可以互换。
- 具体产品:实现抽象产品接口的具体类,每个具体工厂都创建一个具体产品的实例。
3.优点
- 将产品的创建和使用分离开来,使得客户端代码更加简洁,并且向客户端隐藏产品的创建细节。
- 将一系列相关的产品对象的创建工作统一到一个抽象工厂中,客户端只需要访问抽象工厂即可,具 体的产品工厂可以灵活替换。
- 当一个族中的多个对象被设计成一起工作时, 它能够保证客户端始终只使用同一个族中的对象。
4.缺点
- 一个产品族下增加新的产品等级非常困难,甚至需要修改抽象层代码和其下所有的实现, 严重违背了“开闭原则”。
- 增加了代码的复杂性,增加了系统的抽象性,增加了理解难度
5.实现方式太麻烦了,这里就不演示代码了,有兴趣的博主可以自行百度。
二、扩展性和灵活性
1.设定接口规范:抽象类可以定义一组抽象方法,这些方法没有具体实现,要求所有子类必须实现这些方法。这实际上是在设定一个接口规范,确保所有子类都遵循相同的操作接口,即使它们的具体实现可能不同。这提高了系统的规范性和一致性。
2.提供默认实现:除了抽象方法外,抽象类还可以包含具体方法,为子类提供默认行为的实现。这意味着子类可以重用这些实现,或者覆盖它们以提供定制化的功能。这种机制既减少了代码重复,又增加了灵活性,因为子类可以选择性地覆盖或扩展父类的行为。
3.支持多态性:通过抽象类,可以利用多态性原则,编写与具体实现分离的代码。这意味着你可以针对抽象基类编程,而不用担心具体子类的细节。这样,当需要引入新的子类或修改现有子类的实现时,只需保持对外接口不变,即可轻松替换或扩展功能,而不影响到调用这些抽象类的地方。
4.代码组织和模块化 :抽象类有助于将相关的功能组织在一起,形成逻辑上的模块。每个抽象类可以代表一个概念或功能领域,它的子类则代表这个领域内的具体变体。这种组织方式使得代码结构清晰,易于理解和维护,同时也便于团队协作开发。
5.易于扩展:随着项目的发展,需求可能会变化,需要添加新的功能。抽象类作为基础框架,允许你在不破坏原有代码结构的基础上,通过添加新的子类来扩展功能。这种设计模式使得系统更加灵活,能够更好地适应未来的变化。
6.演示:
- 创建一个抽象工厂
public abstract class DbConnectionFactory {// 打开链接public abstract void openConnection(String name);// 关闭链接public abstract void closeConnection(String name);}
- 创建MySQL连接
public class MySQLConnection extends DbConnectionFactory {@Overridepublic void openConnection(String name) {System.out.println(name + ": 打开了MySQL连接");}@Overridepublic void closeConnection(String name) {System.out.println(name + ": 关闭了MySQL连接");}
}
- 创建PgSQL连接
public class PgSQLConnection extends DbConnectionFactory {@Overridepublic void openConnection(String name) {System.out.println(name + ":打开了PgSQL连接");}@Overridepublic void closeConnection(String name) {System.out.println(name + ":关闭了PgSQL连接");}
}
- 测试
public class TestMain {public static void main(String[] args) {DbConnectionFactory mySQLConnection = new MySQLConnection();mySQLConnection.openConnection("用户A");mySQLConnection.closeConnection("用户A");PgSQLConnection pgSQLConnection = new PgSQLConnection();pgSQLConnection.openConnection("用户B");pgSQLConnection.closeConnection("用户B");}
}
三、为什么开发时抽象类用的很少,反而用接口的很多
1.多重继承限制:Java中类只允许单一继承,这意味着一个类只能继承一个抽象类。然而,一个类却可以实现多个接口,这为多继承提供了替代方案,增加了设计的灵活性。因此,当需要实现多个行为规范时,接口成为更自然的选择。
2.多重继承限制:Java中类只允许单一继承,这意味着一个类只能继承一个抽象类。然而,一个类却可以实现多个接口,这为多继承提供了替代方案,增加了设计的灵活性。因此,当需要实现多个行为规范时,接口成为更自然的选择。
3.接口的进化:自从Java 8引入默认方法和静态方法以来,接口变得更加灵活。默认方法允许在接口中提供实现,这样就能够在不破坏现有实现的情况下向接口添加新功能,这在一定程度上弥补了接口与抽象类之间功能上的差距。
4.设计原则:面向接口编程鼓励针对接口而非实现编程,这有利于降低耦合度,提高代码的可测试性和可维护性。接口更纯粹地体现了这一原则,因为它不包含任何状态或具体实现,强调的是行为的约定而非实现细节。
5.框架和库的偏好:现代框架和库,如Spring框架,倾向于使用接口来进行依赖注入,以利用其提供的灵活性和可配置性。这种设计模式鼓励使用接口来定义组件之间的合同。
6.清晰的职责划分:接口通常用于定义类型或行为的契约,而抽象类可能包含更多关于实现细节的逻辑。使用接口可以帮助开发者更清晰地分离关注点,使得代码更容易理解和维护。:自从Java 8引入默认方法和静态方法以来,接口变得更加灵活。默认方法允许在接口中提供实现,这样就能够在不破坏现有实现的情况下向接口添加新功能,这在一定程度上弥补了接口与抽象类之间功能上的差距。
7.设计原则:面向接口编程鼓励针对接口而非实现编程,这有利于降低耦合度,提高代码的可测试性和可维护性。接口更纯粹地体现了这一原则,因为它不包含任何状态或具体实现,强调的是行为的约定而非实现细节。
8.框架和库的偏好:现代框架和库,如Spring框架,倾向于使用接口来进行依赖注入,以利用其提供的灵活性和可配置性。这种设计模式鼓励使用接口来定义组件之间的合同。
9.清晰的职责划分:接口通常用于定义类型或行为的契约,而抽象类可能包含更多关于实现细节的逻辑。使用接口可以帮助开发者更清晰地分离关注点,使得代码更容易理解和维护。