面向对象实践多版本属性

110 阅读4分钟

主旨

利用面向对象去解决一件事

业务

如线程池等相关资源管理器是需要配置属性的,可能在不同的物理机、不同时间段分配需要不同的属性。以此为背景,开发人员在配置文件中设置的不同版本的属性:

executor:
    time:
        weight: 10
        core-size: 5
        max-size: 20
    default:
        weight: 1
        core-size: 5
        max-size: 20

不同版本配置拥有不同权重,权重越高,则优先选择此配置

显然在资源管理器初始化时得到开发人员指定的配置是符合开发人员的想法,如果配置版本是固定,开发人员硬编码争夺配置版本根据权重进行排序然后选择最优版本

但是随着资源管理器的懒加载,在硬编码选择配置与资源管理器初始化之间存在着时间差,又或者资源管理器的二次加载。现在又流行云原生的资源管理,显然在配置的选择是需要动态的、可控制的

硬编码

    public class Property{
        // coreSize、maxSize
        void addCallback(Runnalbe run) // Register a callback to be run if the property is updated.
    }
​
    public Property choose() {
        List<Property> list = new ArrayList<>();
        // add all
        // ... ...
        list.sort(property::weight);
        list.get(0)
    }

结合面向对象去优化硬编码

实现

多版本:使用队列(链表实现)管理不同版本

动态管理:市面上有很多实现好的库

属性基类

public interface ProxyProperty<T> {
​
    T get();
​
    class Factory {
​
        public static <T> ProxyProperty<T> asProperty(final T value) {
            return () -> value;
        }
​
        public static <T> ProxyProperty<T> nullProperty() {
            return () -> null;
        }
​
    }
}
​

动态属性的基类,表示这个值会动态更改

public interface ProxyDynamicProperty<T> extends ProxyProperty<T> {
​
    String getName();
​
    /**
     * Register a callback to be run if the property is updated.
     * Backing implementations may choose to do nothing.
     */
    void addCallback(Runnable callback);
​
}

链式管理动态属性

public class ProxyPropertiesChainedProperty {
    private static final Logger logger = LoggerFactory.getLogger(ProxyPropertiesChainedProperty.class);
​
    /**
     * 链式属性节点的抽象类,添加顺序1->2,链表顺序2->1
     */
    private static abstract class ChainLink<T> {
        /**
         * 确保在并发环境下对next引用的安全更新和访问,当next为null时,表示为最后一个节点,pReference指向this
         */
        private final AtomicReference<ChainLink<T>> pReference;
        private final ChainLink<T> next;
        private final List<Runnable> callbacks;
​
        public abstract String getName();
​
        protected abstract T getValue();
​
        public abstract boolean isValueAcceptable();
​
        public ChainLink() {
            next = null;
            pReference = new AtomicReference<>(this);
            callbacks = new ArrayList<>();
        }
​
        public ChainLink(ChainLink<T> nextProperty) {
            next = nextProperty;
            pReference = new AtomicReference<>(next);
            callbacks = new ArrayList<>();
        }
​
        /**
         * 检查并整理
         * 当属性值发生变化时,将检查当前节点的属性值是否可接受,如果可接受,则更新链式属性为当前节点,否则,更新链式属性为下一个节点
         * 支持添加回调函数,在属性值发生变化时执行
         */
        protected void checkAndFlip() {
            // 判断是否最后一个节点
            if (next == null) {
                pReference.set(this);
                return;
            }
​
            // 判断此节点是否可用,如果可用,则将pReference指向this,否则指向next
            if (this.isValueAcceptable()) {
                logger.debug("Flipping property: {} to use its current value: {}", getName(), getValue());
                pReference.set(this);
            } else {
                logger.debug("Flipping property: {} to use NEXT property: {}", getName(), next);
                pReference.set(next);
            }
​
            for (Runnable r : callbacks) {
                r.run();
            }
        }
​
        /**
         * 返回最后一个节点的值
         */
        public T get() {
            if (pReference.get() == this) {
                return this.getValue();
            } else {
                return pReference.get().get();
            }
        }
​
        /**
         * @param r callback to execut
         */
        public void addCallback(Runnable r) {
            callbacks.add(r);
        }
​
        /**
         * @return String
         */
        public String toString() {
            return getName() + " = " + get();
        }
    }
​
    private static class ChainProperty<T> extends ChainLink<T> {
​
        private final ProxyDynamicProperty<T> sProp;
​
        private ChainProperty(ProxyDynamicProperty<T> sProp) {
            super();
            this.sProp = sProp;
        }
​
        public ChainProperty(ProxyDynamicProperty<T> sProperty, ChainProperty<T> next) {
            super(next);
​
            sProp = sProperty;
​
            sProperty.addCallback(() -> {
                logger.debug("Property changed: '{} = {}'", getName(), getValue());
                checkAndFlip();
            });
​
            checkAndFlip();
        }
​
        @Override
        protected T getValue() {
            return sProp.get();
        }
​
        @Override
        public String getName() {
            return sProp.getName();
        }
​
        @Override
        public boolean isValueAcceptable() {
            return (sProp.get() != null);
        }
    }
​
​
    private static <T> ChainBuilder<T> forType(final Class<T> type) {
        return new ChainBuilder<T>() {
            @Override
            protected Class<T> getType() {
                return type;
            }
        };
    }
​
​
    public static ChainBuilder<Integer> forInteger() {
        return forType(Integer.class);
    }
​
    public static abstract class ChainBuilder<T> {
​
        private ChainBuilder() {
            super();
        }
​
        private List<ProxyDynamicProperty<T>> properties = new ArrayList<>();
​
​
        public ChainBuilder<T> add(ProxyDynamicProperty<T> property) {
            properties.add(property);
            return this;
        }
​
        public ChainBuilder<T> add(String name, T defaultValue) {
            properties.add(getDynamicProperty(name, defaultValue, getType()));
            return this;
        }
​
        public ProxyDynamicProperty<T> build() {
            if (properties.size() < 1) throw new IllegalArgumentException();
            if (properties.size() == 1) return properties.get(0);
​
            List<ProxyDynamicProperty<T>> reversed = new ArrayList<>(properties);
            Collections.reverse(reversed);
​
            ChainProperty<T> current = null;
            for (ProxyDynamicProperty<T> p : reversed) {
                if (current == null) {
                    current = new ChainProperty<>(p);
                } else {
                    current = new ChainProperty<>(p, current);
                }
            }
​
            return new ChainProxyProperty<>(current);
        }
​
        protected abstract Class<T> getType();
​
    }
​
    /**
     * 多版本(链表)动态属性代理类
     */
    private static class ChainProxyProperty<T> implements ProxyDynamicProperty<T> {
        private final ChainProperty<T> property;
​
        public ChainProxyProperty(ChainProperty<T> property) {
            super();
            this.property = property;
        }
​
        @Override
        public String getName() {
            return property.getName();
        }
​
        @Override
        public T get() {
            return property.get();
        }
​
        @Override
        public void addCallback(Runnable callback) {
            property.addCallback(callback);
        }
​
    }
​
    private static <T> ProxyDynamicProperty<T> getDynamicProperty(String propName, T defaultValue, Class<T> type) {
        // 动态属性案例、建议使用成熟库比如Spring支持的Scope cache
        return new ProxyDynamicProperty<>() {
            @Override
            public String getName() {
                return propName;
            }
​
            @Override
            public void addCallback(Runnable callback) {
            }
​
            @Override
            public T get() {
                return defaultValue;
            }
        };
    }
}
​

使用

        ProxyDynamicProperty<Integer> chainedProperty = ProxyPropertiesChainedProperty
                .forInteger()
                .add("prop1", 10)
                .add("prop2", 20)
                .build();
​
        Assert.assertEquals("10", String.valueOf(chainedProperty.get()));
​
        // 可使用另一个add API,自定义动态属性的实现

总结

很明显,使用面向对象去解决这个问题增加了设计复杂性,需要合理地划分类和对象,并建立它们之间的关系。如果设计不当,类之间的关系可能变得复杂,导致代码难以理解和维护

但是再去看看使用面向对象的优势

  • 扩展性:通过引入多版本和动态管理的概念,面向对象设计实现了属性链式管理。这种设计允许在运行时动态选择合适的属性配置,并支持注册回调函数以便在属性更新时执行。因此,系统可以方便地添加新的属性版本或扩展现有的属性管理功能
  • 模块化和封装性:使用者持有ProxyDynamicProperty。而开发者的代码更易于理解和维护,并隐藏了实现细节