java 动态属性的实现

您所在的位置:网站首页 java动态添加属性和方法 java 动态属性的实现

java 动态属性的实现

#java 动态属性的实现| 来源: 网络整理| 查看: 265

文章目录 前言一、普遍的普遍的-类定义使用示例 普遍的-类改造1使用示例 普遍的-类改造2使用示例 二、较好的动态属性接口类定义使用示例 三、良好的示例类的扩展属性文件 BirdAttrOption业务类动态属性项集 - AttrOptions属性项 - AttrOption动态属性接口 - AttrOptionDynamic 总结对类型明确的说明对组合的说明之后 最后

前言

在 java 中,如何让一个类具有动态属性。这里将介绍一种技巧,可以使得你的类,具有良好的动态属性的能力。普遍的做法是在类中申明一个 map 属性,把想要扩展的属性放入这个 map 中,这样就可以使得类具有动态属性的能力了。本文介绍的实现上本质也是如此,看到这里你是不是已经没兴趣往下看了,兄弟,先别着急,如果仅是样我也没必要写这个了。这里介绍的是具有良好的动态属性的能力,看完本文,你会获得很大的收益!

这里会介绍三种动态属性的实现方式

普遍的较好的良好的

本文会循序渐进的从普遍的、较好的、良好的顺序来讲代码的演化过程。

一、普遍的 普遍的-类定义

类中申明一个 map 属性,把想要扩展的属性放入这个 map 中,这样就可以使得类具有动态属性的能力了。

@FieldDefaults(level = AccessLevel.PRIVATE) public class BirdAttr { /** 动态属性 */ final Map attr = new HashMap(); public void setAttr(String attrName, Object value) { this.attr.put(attrName, value); } public Object getAttr(String attrName) { return this.attr.get(attrName); } } 使用示例 class BirdAttrTest { public void test1() { BirdAttr bird = new BirdAttr(); // 设置属性 bird.setAttr("name","塔姆"); bird.setAttr("age", 18); // 获取属性 String name = (String) bird.getAttr("name"); int age = (int) bird.getAttr("age"); } }

通过使用示例,我们可以看到,每次使用属性时都需要进行一次强转。

为了避免强转,我们有必要对这个类进行一次改造

普遍的-类改造1

我们加了一些方法,这些方法的目的在于,当我们使用动态属性时可以省去强转的一个步骤。

@FieldDefaults(level = AccessLevel.PRIVATE) public class BirdAttr { /** 动态属性 */ final Map attr = new HashMap(); public void setAttr(String attrName, Object value) { this.attr.put(attrName, value); } public Object getAttr(String attrName) { return this.attr.get(attrName); } public String getAttrString(String attrName) { return (String) this.attr.get(attrName); } public Integer getAttrInt(String attrName) { return (Integer) this.attr.get(attrName); } } 使用示例 class BirdAttrTest { public void test2() { BirdAttr bird = new BirdAttr(); // 设置属性 bird.setAttr("name","塔姆"); bird.setAttr("age", 18); // 获取属性 String name = bird.getAttrString("name"); int age = bird.getAttrInt("age"); } }

这样看起来舒服多了(至少在使用者的角度),毕竟舒服是相对的,相对于上面的示例。

但细心的朋友会发现,每个类型都需要声明一个对应的类型转换方法。比如 int、bool、long、String、byte、short、char、double…等。这样做确实太麻烦,当然我们还可以使用泛型来确定类型,修改如下

普遍的-类改造2 @FieldDefaults(level = AccessLevel.PRIVATE) public class BirdAttr { /** 动态属性 */ final Map attr = new HashMap(); public void setAttr(String attrName, Object value) { this.attr.put(attrName, value); } /** 泛型来确定类型 */ public T getAttr(String attrName) { return (T) this.attr.get(attrName); } } 使用示例 class BirdAttrTest { public void test3() { BirdAttr bird = new BirdAttr(); // 设置属性 bird.setAttr("name","塔姆"); bird.setAttr("age", 18); // 获取属性 String name = bird.getAttr("name"); int age = bird.getAttr("age"); } }

看起来似乎很完美,省去了类型的转换的同时使用起来也简洁了些。好了,到这里动态属性介绍完了 (开玩笑的)!

你会发现这个动态属性只属于这一个类,如果还有一个类也想拥有动态属性的功能呢?copy 在来一次是不可能的,但我们可以用接口的方式,也就是接下来要说的 较好的。

二、较好的 动态属性接口

用接口的方式来实现动态属性,可以使得实现接口的类都具有现动态属性的功能。

public interface AttrDynamic { /** * 获取动态成员属性map * * @return 动态成员属性map */ Map getAttr(); /** * 获取动态成员属性 * * @param attrName 属性名 * @param t * @return val */ default T getAttr(String attrName) { Map map = getAttr(); return (T) map.get(attrName);; } } 类定义

只需要实现接口,就能拥有动态属性的功能,非常的方便。但你会发现接口中是需要子类实现一个 getAttr() 方法的,类中没有重写 getAttr() 方法,确能够运行是因为巧妙的运用的 lombok 的 Getter 注解,由于 lombok 的这个注解会为属性提供的 getter 方法,正好能对得上接口的方法,所以就不需要显式的重写接口的 getAttr( ) 方法了。

@Getter @FieldDefaults(level = AccessLevel.PRIVATE) public class BirdAttrDynamic implements AttrDynamic { /** 动态属性 */ final Map attr = new HashMap(); } 使用示例 class BirdAttrDynamicTest { public void test1() { BirdAttrDynamic bird = new BirdAttrDynamic(); // 设置属性 bird.setAttr("name", "塔姆"); bird.setAttr("age", 18); // 获取属性 String name = bird.getAttr("name"); int age = bird.getAttr("age"); } }

用接口的方式来实现动态属性有很多好处:

类只要实现接口就能拥有动态属性的功能。即使每个类型都需要声明一个对应的类型转换方法,比如上面普遍中提到的 int、bool、long、String、byte、short、char、double…等转换方法,我们只需要在动态属性接口中增加default 的方法就可以了(只维护接口)。如果使用【普遍的】方式中改造,假设有10个类需要动态属性,那么你需要修改10个类。

从这里可以看出,【普遍的和较好的】在动态属性的实现方式中,都有一个很大的问题,我们先称为下一任维护者问题、华丽的简洁。

华丽的简洁这里指的是初次看上去的示例使用很简洁且,仿佛没什么毛病。但真正在实际中运用后才会发现缺陷,直至下一任维护者的运来问题暴露得更加疯狂。

class BirdQquestionTest { public void test1() { BirdAttrDynamic bird = new BirdAttrDynamic(); // 假设你是下一任的维护者,你知道这个属性名的类型吗 bird.getAttr("fuck"); } }

从上面的示例中,你无法知道属性 “fuck” 的具体类型,这就是华丽的简洁。如果你想知道,只能阅读该项目其他地方的源码来寻找。实际中你的上一任哥们用这种方式编码可嗨了,你的上一任嗨一分,给你带来的痛苦大概是 2.25分(根据前景理论得出,该理论是行为经济学的重大成果之一)。

无论是普遍的还是较好的在动态属性的实现方式中,我们的下一任维护者无法很快的知道这个属性的确切类型。因为属性太动态的原因,为了可维护性我们需要尽量不要这么做。

当然,到这里你也可以说我们可以先定义一个类或者接口,把动态属性的属性名放到这个文件中。类似下面这样

public interface BirdAttrName { /** name 的类型是 string */ String name = "name"; /** age 的类型是 int */ String age = "age"; /** fuck 的类型是 Fuck.java 类 */ String fuck = "fuck"; }

这个属性文件只是理想化的产物,你虽然可以在项目团队中定这些规范,让团队成员遵守。但你很难让每个成员都严格遵守(特别是后进的新成员),比如忘记写类型注释了或者说写错了类型注释呢,所以更别说你的上一任了(类似你接手的二手项目)。

那么还有较好的方式来避免这些吗?当然是有的,就是下面介绍的这种 良好的 方式

三、良好的 示例

为了避免一些枯燥,这次我们先看 良好的 方式的使用示例

class BirdAttrOptionDynamicTest { public static void main(String[] args) { BirdAttrOptionDynamic bird = new BirdAttrOptionDynamic(); // 设置属性 bird.option(BirdAttrOption.name, "塔姆"); bird.option(BirdAttrOption.age, 19); // 获取属性 String name = bird.option(BirdAttrOption.name); int age = bird.option(BirdAttrOption.age); } }

从示例中,我们可以动态的增加属性,动态的获取属性的值,并且没有强制转换。属性名由 BirdAttrOption.java 统一来管理。

类的扩展属性文件 BirdAttrOption interface BirdAttrOption { AttrOption name = AttrOption.valueOf("name"); AttrOption age = AttrOption.valueOf("age"); }

可以看见,在 BirdAttrOption.java 中,我们提前添加了一些需要扩展的属性名,并且类型的明确的。属性的信息由 AttrOption.java 来明确。(就算那些家伙忘记写类型注释或者说写错了类型注释,也没关系)

业务类

BirdAttrOptionDynamic 这个是我们自定义的一个业务类,只需要实现 AttrOptionDynamic 接口就能具备动态属性的功能。

@Getter @FieldDefaults(level = AccessLevel.PRIVATE) public class BirdAttrOptionDynamic implements AttrOptionDynamic { /** 动态属性项集 */ final AttrOptions options = new AttrOptions(); } 动态属性项集 - AttrOptions

用于存放动态属性的地方,对多个属性的管理

class AttrOptions { /** 动态成员属性 */ final Map options = new HashMap(); /** 获取值 */ @SuppressWarnings("unchecked") public T option(AttrOption option) { return (T) options.get(option); } /** 设置值 */ public void option(AttrOption option, T value) { options.put(option, value); } } 属性项 - AttrOption

属性项, 用于确定属性的名和类型。

record 是值类型,好像是 java14 的出的(具体忘记的),转为java类的大概意思就是类中声明了一个属性 name,并自动提供 getter 方法。

record AttrOption(String name) { /** 构建属性项 */ public static AttrOption valueOf(String name) { return new AttrOption(name); } } 动态属性接口 - AttrOptionDynamic interface AttrOptionDynamic { /** 动态成员属性 */ AttrOptions getOptions(); /** 获取值 */ default T option(AttrOption option) { return this.getOptions().option(option); } /** 设置值 */ default void option(AttrOption option, T value) { this.getOptions().option(option, value); } } 总结

好的到这里就讲完了,良好的实现方式就是这样。(开玩笑的)

张三:凭什么你这个就是良好的动态属性实现方式。

OK!似乎张三提了一个问题。

在【良好的】与【普遍的和较好的】实现方案相比较,我们增加了两个类 AttrOption 和 AttrOptions。本来一个 map 可以解决的事为什么要多增加两个类呢。答案是组合与类型明确,类型明确可能会好理解一些,但组合是什么鬼。

对类型明确的说明

在类的扩展属性文件 BirdAttrOption.java 中,属性名由 BirdAttrOption.java 统一来管理。即使忘记写类型注释了也没关系,因为已经明确类型了(我们在声明属性时就能明确类型了)。意味着你的团队成员 ”不能很轻松的“ 使用动态属性了,毕竟直接的敲字符总是轻松的。如

class BirdAttrTest { public void test1() { BirdAttr bird = new BirdAttr(); // 设置属性 bird.setAttr("name","塔姆"); bird.setAttr("age", 18); // 获取属性 String name = (String) bird.getAttr("name"); int age = (int) bird.getAttr("age"); // test option -- error、error、error BirdAttrOptionDynamic bird = new BirdAttrOptionDynamic(); // ======== 不能轻松的使用字符来当属性了, 下面的使用方式会在工具中报错 ======== // 因为不支持这样做 bird.option("name", "塔姆"); } }

我们用代码级别来约束团队的成员 (此时就变成你可以不听团队的规范,但工具不允许你这样做)。

对组合的说明

我们增加了两个类 AttrOption 和 AttrOptions,本来一个 map 可以解决的事为什么要多增加两个类呢?

复用:组合是在Java中实现程序复用(reusibility)的基本手段之一。

单一职责:一个类只做一件事

AttrOption:负责属性名和类型明确,实际上我们还可以扩展一些默认值。

AttrOptions:负责管理 AttrOption

类的复杂性降低,实现什么职责都有明确的定义; 逻辑变得简单,类的可读性提高了,而且,因为逻辑简单,代码的可维护性也提高了; 变更的风险降低,因为只会在单一的类中的修改。 类的每个责任都有改变的潜在区域。超过一个责任,意味着超过一个改变的区域。 这个原则告诉我们,尽量让每一个类保持单一责任。

对于 AttrOptions 和 AttrOption 我们还可以单独的拿出来使用,就像下面这样。

在测试的角度也更轻了些!

class BirdAttrOptionDynamicTest { public void testAttrOptions() { AttrOption love = AttrOption.valueOf("love"); final AttrOptions options = new AttrOptions(); options.option(love, 777L); Long loveValue = options.option(love); System.out.println(loveValue); } }

通过 “良好的 ” 动态属性实现方式,我们做到了类型的明确

我们增加了两个类 AttrOption 和 AttrOptions,做到了规范编码、单一职责、复用,真棒!

之后

我们还想让其他类具有动态属性,只需实现接口和声明一个 AttrOptions 变量就可以了,是不是很简单。

@Getter @FieldDefaults(level = AccessLevel.PRIVATE) public class TigerAttrOptionDynamic implements AttrOptionDynamic { /** 动态属性项集 */ final AttrOptions options = new AttrOptions(); }

源码参考地址:

网络游戏框架-文档

网络游戏框架-源码

最后

本文可以转载,但必须保留所有内容。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3