Java中简化Campaign与Character相似类操作的最佳实践咨询
解决方案:基于抽象类+接口的代码复用方案
针对你遇到的Campaign和Character类代码冗余问题,推荐用抽象类封装共享逻辑+接口定义差异字段契约的方案,既保留两个类的独立性,又能最大化复用代码,还支持后续扩展。
核心思路
- 用抽象类把ID、Owner、Name这些共享字段和通用操作(比如JSON序列化/反序列化的通用部分)封装起来,把差异字段的处理交给子类。
- 定义接口统一差异字段的访问方式,让两个类通过接口暴露差异字段,通用代码只需要调用接口方法,不用关心具体字段名。
- 写一个通用管理器类,基于抽象类和接口实现加载、保存等通用操作,彻底消除重复代码。
具体实现代码
1. 抽象类封装共享逻辑
创建DndEntity抽象类,负责处理所有共享字段和通用JSON操作:
import org.json.JSONObject; import java.io.FileWriter; import java.io.IOException; public abstract class DndEntity { protected String id; protected String owner; protected String name; // 共享字段的getter/setter public String getId() { return id; } public void setId(String id) { this.id = id; } public String getOwner() { return owner; } public void setOwner(String owner) { this.owner = owner; } public String getName() { return name; } public void setName(String name) { this.name = name; } // 通用保存方法,子类只需实现差异字段的序列化 public void saveToJson(String filePath) { JSONObject json = new JSONObject(); // 序列化共享字段 json.put("id", id); json.put("owner", owner); json.put("name", name); // 交给子类处理差异字段 addVariantFields(json); // 写入文件的通用逻辑 try (FileWriter writer = new FileWriter(filePath)) { writer.write(json.toString(4)); } catch (IOException e) { e.printStackTrace(); } } // 抽象方法:子类实现差异字段的序列化 protected abstract void addVariantFields(JSONObject json); }
2. 接口定义差异字段契约
创建DndEntityWithAssociates接口,统一差异字段的访问方式:
import java.util.List; public interface DndEntityWithAssociates { // 获取"Edition/Race"这类变体属性 String getVariantAttribute(); void setVariantAttribute(String value); // 获取关联实体列表(Campaign的Characters / Character的Campaigns) List<? extends DndEntity> getAssociatedEntities(); void setAssociatedEntities(List<? extends DndEntity> entities); }
3. 子类实现接口并映射字段
Campaign类
import org.json.JSONArray; import org.json.JSONObject; import java.util.List; public class Campaign extends DndEntity implements DndEntityWithAssociates { private String edition; private List<Character> characters; @Override protected void addVariantFields(JSONObject json) { json.put("edition", edition); // 序列化关联的角色列表(仅存ID避免循环引用) JSONArray charsArray = new JSONArray(); for (Character c : characters) { charsArray.put(c.getId()); } json.put("characters", charsArray); } @Override public String getVariantAttribute() { return edition; } @Override public void setVariantAttribute(String value) { this.edition = value; } @Override public List<? extends DndEntity> getAssociatedEntities() { return characters; } @Override public void setAssociatedEntities(List<? extends DndEntity> entities) { this.characters = (List<Character>) entities; } // 后续扩展的特有方法 public void addCharacter(Character character) { characters.add(character); } }
Character类
import org.json.JSONArray; import org.json.JSONObject; import java.util.List; public class Character extends DndEntity implements DndEntityWithAssociates { private String race; private List<Campaign> campaigns; @Override protected void addVariantFields(JSONObject json) { json.put("race", race); // 序列化关联的战役列表 JSONArray campaignsArray = new JSONArray(); for (Campaign c : campaigns) { campaignsArray.put(c.getId()); } json.put("campaigns", campaignsArray); } @Override public String getVariantAttribute() { return race; } @Override public void setVariantAttribute(String value) { this.race = value; } @Override public List<? extends DndEntity> getAssociatedEntities() { return campaigns; } @Override public void setAssociatedEntities(List<? extends DndEntity> entities) { this.campaigns = (List<Campaign>) entities; } // 后续扩展的特有方法 public void joinCampaign(Campaign campaign) { campaigns.add(campaign); } }
4. 通用管理器类
写一个DndEntityManager,实现通用的加载、保存操作:
import org.json.JSONArray; import org.json.JSONObject; import org.json.JSONTokener; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class DndEntityManager { // 通用加载方法,支持加载Campaign或Character public <T extends DndEntity & DndEntityWithAssociates> T loadEntity(Class<T> clazz, String filePath) { try (FileReader reader = new FileReader(filePath)) { JSONObject json = new JSONObject(new JSONTokener(reader)); T entity = clazz.getDeclaredConstructor().newInstance(); // 设置共享字段 entity.setId(json.getString("id")); entity.setOwner(json.getString("owner")); entity.setName(json.getString("name")); // 设置变体属性 String variantKey = clazz == Campaign.class ? "edition" : "race"; entity.setVariantAttribute(json.getString(variantKey)); // 加载关联实体(示例仅处理ID,实际可根据ID从对应文件加载) String associateKey = clazz == Campaign.class ? "characters" : "campaigns"; JSONArray associateIds = json.getJSONArray(associateKey); List<DndEntity> associates = new ArrayList<>(); // 这里可以根据ID加载对应的实体,比如如果是Campaign,加载Character对象 // 示例省略具体加载逻辑 entity.setAssociatedEntities(associates); return entity; } catch (Exception e) { e.printStackTrace(); return null; } } // 通用保存方法 public <T extends DndEntity> void saveEntity(T entity, String filePath) { entity.saveToJson(filePath); } }
扩展说明
- 如果后续要给Campaign加新字段(比如
setting),或者给Character加characterClass,只需要在对应类里添加字段,修改addVariantFields方法即可,完全不影响抽象类和管理器类。 - 两个类各自的特有方法可以自由扩展,完全保留独立性,不会出现合并成通用类的扩展性问题。
内容的提问来源于stack exchange,提问作者Patrick
相关产品推荐
相关产品推荐

