
在许多系统设计中,为不同类型的实体生成唯一且具有特定结构规则的标识符(ID)是一项常见需求。本教程将解决一个具体场景:设计一个物品(Item)的类层次结构,其中包含Flight(飞行)和Payload(载荷)两大抽象子类,以及更具体的CommercialFlight(商业航班)、IndustrialFlight(工业航班)、Person(人员)和Cargo(货物)等具体子类。所有Item都需要一个9位长的唯一ID,其生成规则如下:
这个规则的核心挑战在于如何将子类特有的前缀与全局递增的后缀有效结合。
初学者在解决此类问题时,可能会尝试在基类的构造函数中使用this instanceof判断当前实例的类型,并为每个类型维护一个独立的计数器,如下所示:
public abstract class Item {
protected int id;
public Item() {
int commercialID = 100000000; // 局部变量
int industrialID = 200000000; // 局部变量
if( this instanceof Commercial){ // 假设 Commercial 是具体类
id = commercialID;
commercialID++; // 递增的是局部变量的副本
} else if (this instanceof Industrial) { // 假设 Industrial 是具体类
id = industrialID;
industrialID++; // 递增的是局部变量的副本
}
}
}这种方法存在以下几个主要问题:
立即学习“Java免费学习笔记(深入)”;
为了克服这些局限性,我们需要一种更优雅、更具扩展性的面向对象解决方案。
解决此问题的关键在于利用Java的抽象类、静态变量和多态性。
Item类是整个ID生成机制的核心。它将包含ID的存储、全局计数器以及组合ID的逻辑。
public abstract class Item {
protected int id; // 存储生成的ID
protected static int lastGeneratedId = 0; // 静态变量,用于存储全局递增的后缀
protected Item() {
// 每次创建新的Item实例时,全局计数器递增
lastGeneratedId++;
// 组合ID:(前缀 * 100000000) + 后缀
// 注意:这里100000000是为了将前缀放到9位ID的首位,后8位留给lastGeneratedId
this.id = 100000000 * getIdPrefix() + lastGeneratedId;
System.out.println(toString()); // 打印创建信息,便于调试
}
// 抽象方法:强制子类实现,返回其对应的ID前缀
protected abstract int getIdPrefix();
@Override
public String toString() {
return "New " + getClass().getName() + " created with id " + id;
}
}解释:
为了更好地组织类层次结构,我们引入了Flight和Payload这两个抽象子类。它们继承自Item,但由于它们本身也是抽象的,所以不需要实现getIdPrefix()方法。它们的主要作用是提供更具体的类型分组。
// Flight 抽象子类
public abstract class Flight extends Item {
public Flight() {
super(); // 调用父类Item的构造函数
}
}
// Payload 抽象子类
public abstract class Payload extends Item {
public Payload() {
super(); // 调用父类Item的构造函数
}
}现在,我们来实现具体的子类。每个具体子类都需要继承其相应的抽象父类,并实现getIdPrefix()方法,返回其类型对应的ID前缀。
// 商业航班
public class CommercialFlight extends Flight {
public CommercialFlight() {
super(); // 调用父类Flight的构造函数,进而调用Item的构造函数
}
@Override
protected int getIdPrefix() {
return 1; // 商业航班的前缀是1
}
}
// 工业航班
public class IndustrialFlight extends Flight {
public IndustrialFlight() {
super();
}
@Override
protected int getIdPrefix() {
return 2; // 工业航班的前缀是2
}
}
// 人员
public class Person extends Payload {
public Person() {
super();
}
@Override
protected int getIdPrefix() {
return 3; // 人员的前缀是3
}
}
// 货物
public class Cargo extends Payload {
public Cargo() {
super();
}
@Override
protected int getIdPrefix() {
return 4; // 货物的前缀是4
}
}为了验证上述设计,我们创建一个ItemTester类来实例化不同类型的Item。
public class ItemTester {
public static void main(String[] args) {
System.out.println("--- Creating Items ---");
new Cargo(); // 第一个Item
new IndustrialFlight(); // 第二个Item
new Cargo(); // 第三个Item
new CommercialFlight(); // 第四个Item
new IndustrialFlight(); // 第五个Item
new Person(); // 第六个Item
new Person(); // 第七个Item
System.out.println("--- All Items created ---");
}
}运行结果示例:
--- Creating Items --- New Cargo created with id 400000001 New IndustrialFlight created with id 200000002 New Cargo created with id 400000003 New CommercialFlight created with id 100000004 New IndustrialFlight created with id 200000005 New Person created with id 300000006 New Person created with id 300000007 --- All Items created ---
从输出结果可以看出,ID的前缀正确地根据子类类型变化,而后八位数字(从1开始,因为lastGeneratedId在构造函数中首先递增)则全局递增,完美符合了设计要求。
线程安全: 在多线程环境下,lastGeneratedId++操作可能存在竞态条件(race condition),导致ID生成不连续或重复。为了确保线程安全,可以使用java.util.concurrent.atomic.AtomicInteger类,或者在递增操作外部使用synchronized关键字。
// 线程安全版本
import java.util.concurrent.atomic.AtomicInteger;
public abstract class Item {
protected int id;
// 使用AtomicInteger确保lastGeneratedId在多线程环境下的原子性递增
protected static AtomicInteger lastGeneratedId = new AtomicInteger(0);
protected Item() {
// 获取当前值并递增,然后使用递增后的值
this.id = 100000000 * getIdPrefix() + lastGeneratedId.incrementAndGet();
System.out.println(toString());
}
// ... 其他代码不变
}ID持久化: 如果系统需要重启,并且ID的递增序列需要从上次关闭时的状态继续,那么lastGeneratedId的值需要被持久化。这通常涉及将其存储到数据库、文件或缓存中,并在系统启动时加载其最新值。
ID长度与类型: 当前的ID是9位数字,完全可以存储在int类型中。如果ID的位数更多,或者需要包含字母等非数字字符,则需要考虑使用long类型或String类型来存储ID。
起始值: lastGeneratedId的初始值设置为0,在构造函数中先递增再使用,因此第一个ID的后缀是1。如果要求第一个ID后缀是0,则需要调整lastGeneratedId的初始值或递增逻辑。例如,将lastGeneratedId初始化为-1,或在组合ID时使用lastGeneratedId.getAndIncrement()。
通过上述设计和实现,我们成功构建了一个灵活、可扩展且符合面向对象原则的ID生成系统,能够满足复杂规则下的唯一标识符生成需求。
以上就是Java中基于继承和多态实现动态ID生成策略的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号