当前位置: 首页 > news >正文

wordpress 不能查看站点wordpress控制台地址

wordpress 不能查看站点,wordpress控制台地址,明天网页游戏开服表,好看的图案设计简介#xff1a; 数字农业库存管理系统在2020年时#xff0c;部门对产地仓生鲜水果生产加工数字化的背景下应运而生。项目一期的数农WMS中的各类库存操作均为单独编写。而伴随着后续的不断迭代#xff0c;这些库存操作间慢慢积累了大量的共性逻辑#xff1a;如参数校验、幂…简介 数字农业库存管理系统在2020年时部门对产地仓生鲜水果生产加工数字化的背景下应运而生。项目一期的数农WMS中的各类库存操作均为单独编写。而伴随着后续的不断迭代这些库存操作间慢慢积累了大量的共性逻辑如参数校验、幂等性控制、操作明细构建、同步任务构建、数据库操作CAS重试、库存动账事件发布等等……大量重复或相似的代码不利于后续维护及高效迭代因此我们决定借鉴并比较模板方法Template Method和回调Callback的思路进行重构我们需要为各类库存操作搭建一个统一的框架对其中固定不变的共性逻辑进行复用而对会随场景变化的部分提供灵活扩展的能力支持。 作者 | 在田 来源 | 阿里技术公众号 一 问题背景 数字农业库存管理系统以下简称数农WMS是在2020年时部门对产地仓生鲜水果生产加工数字化的背景下应运而生。项目一期的数农WMS中的各类库存操作如库存增加、占用、转移等均为单独编写。而伴随着后续的不断迭代这些库存操作间慢慢积累了大量的共性逻辑如参数校验、幂等性控制、操作明细构建、同步任务构建、数据库操作CAS重试、库存动账事件发布等等……大量重复或相似的代码不利于后续维护及高效迭代因此我们决定借鉴并比较模板方法Template Method和回调Callback的思路进行重构我们需要为各类库存操作搭建一个统一的框架对其中固定不变的共性逻辑进行复用而对会随场景变化的部分提供灵活扩展的能力支持。 二 模板方法 GoF的《设计模式》一书中对模板方法的定义是「定义一个操作中的算法的骨架而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。」 —— 其核心是对算法或业务逻辑骨架的复用以及其中部分操作的个性化扩展。在正式介绍对数农WMS库存操作的重构工作前我们先以一个具体案例 —— AbstractQueuedSynchronizer(注1)以下简称AQS —— 来了解模板方法设计模式。虽然通过AQS这个相对复杂的例子来介绍模板方法显得有些小题大做但由于AQS一方面是Java并发包的核心框架另一方面也是模板方法在JDK中的现实案例对它的剖析能使我们了解其背后精心的设计思路同时与下文将介绍的回调的重构方式进行对比值得我们多花一些时间研究。 《Java并发编程实战》中对AQS的描述是AQS是一个用于构建锁和同步器的框架许多同步器都可以通过AQS很容易并且高效地构造出来。不仅ReentrantLock和Semaphore是基于AQS构建的还包括CountDownLatch、ReentrantReadWriteLock等。AQS解决了在实现同步器时涉及的大量细节问题例如等待线程采用FIFO队列操作顺序。在基于AQS构建的同步器类中最基本的操作包括各种形式的「获取操作」和「释放操作」。在不同的同步器中可以定义一些灵活的标准来判断某个线程是应该通过还是需要等待。比如当使用锁或信号量时获取操作的含义就很直观即「获取的是锁或者许可」。AQS负责管理同步器类中的状态synchronization state它管理了一个整数状态信息用于表示任意状态。例如ReentrantLock用它来表示所有者线程已经重复获取该锁的次数Semaphore用它来表示剩余的可被获取的许可数量。 对照我们在前文中引用的GoF对模板模式的定义这里提到的「锁和同步器的框架」即对应「算法的骨架」「灵活的标准」即对应「重定义该算法的某些特定步骤」而synchronization state以下简称「同步状态」可以说是这两者之间交互的桥梁。Doug Lea对AQS框架的「获取操作」和「释放操作」的算法骨架的基本思路描述如下方伪代码所示。可以看到在获取和释放操作中对同步状态的判断和更新是算法骨架中可被各类同步器灵活扩展的部分而相应的对操作线程的入队、阻塞、唤起和出队操作则是算法骨架中被各类同步器所复用的部分。 // 「获取操作」伪代码 While(synchronization state does not allow acquire) { // * 骨架扩展点enqueue current thread if not already queued; // 线程结点入队possibly block current thread; // 阻塞当前线程 } dequeue current thread if it was queued; // 线程结点出队// 「释放操作」伪代码 update synchronization state // * 骨架扩展点 if (state may permit a blocked thread to acquire) { // * 骨架扩展点unblock one or more queued threads; // 唤起被阻塞的线程 } 下面我们以大家熟悉的ReentrantLock为例具体分析。ReentrantLock实例内部维护了一个AQS的具体实现用户的lock/unlock请求最终是借助AQS实例的acquire/release方法实现。同时AQS实例在被构造时有两种选择非公平性锁实现和公平性锁实现。我们来看下AQS算法骨架部分的代码 // AQS acquire/release 操作算法骨架代码 public abstract class AbstractQueuedSynchronizerextends AbstractOwnableSynchronizerimplements java.io.Serializable {// 同步状态 synchronization state private volatile int state; // 排他式「获取操作」public final void acquire(int arg) {if (!tryAcquire(arg) // * 骨架扩展点acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 线程结点入队selfInterrupt();}// 针对已入队线程结点的排他式「获取操作」final boolean acquireQueued(final Node node, int arg) {boolean failed true;try {boolean interrupted false;for (;;) {final Node p node.predecessor();if (p head tryAcquire(arg)) { // * 骨架扩展点setHead(node); // 线程结点出队队列head为哑结点p.next null;failed false;return interrupted;}if (shouldParkAfterFailedAcquire(p, node) parkAndCheckInterrupt()) // 阻塞当前线程interrupted true;}} finally {if (failed)cancelAcquire(node);}}// 排他式「释放操作」public final boolean release(int arg) {if (tryRelease(arg)) { // * 骨架扩展点Node h head;if (h ! null h.waitStatus ! 0)unparkSuccessor(h); // 唤起被阻塞的线程return true;}return false;}// * 排他式「获取操作」骨架扩展点protected boolean tryAcquire(int arg) {throw new UnsupportedOperationException();}// * 排他式「释放操作」骨架扩展点protected boolean tryRelease(int arg) {throw new UnsupportedOperationException();}} 可以看到AQS骨架代码为其子类的具体实现封装并屏蔽了复杂的FIFO队列和线程控制逻辑。ReentrantLock中的AQS实例只需实现其中的个性化逻辑部分tryAcquire和tryRelease方法。比如在tryAcquire方法中如果发现同步状态为0会尝试以CAS的方式更新同步状态为1以获取锁如果发现同步状态大于0且当前线程就是持有锁的线程则会将同步状态加1表示锁的重入否则方法返回false表示获取锁失败。而其中非公平性锁ReentrantLock.NonfairSync和公平性锁ReentrantLock.FairSync的区别主要在于公平性锁在尝试获取锁时会检查是否已有其他线程先于当前线程等待获取锁如果没有才会按照前述的方式尝试加锁。下图是ReentrantLock中AQS具体实现的类图中间有一层额外的ReentrantLock.Sync主要是为了部分代码的复用而设计。 三 回调方式 但是数农WMS最终使用的重构方式实际上并不是模板方法模式而是借鉴了Spring的风格基于回调Callback的方式实现算法骨架中的扩展点。维基百科中对回调的定义是「一段可执行代码被作为参数传递到另一段代码中并将在某个时机被这段代码回调执行」。回调虽然不属于GoF的书中总结的某种特定的设计模式但是在观察者Observer、策略Strategy和访问者Visitor这些模式中都可以发现它的身影注2可以说是一种常见的编程方式。 如下述RedisTemplate中的管道模式命令执行方法其中的RedisCallback ? action参数即是作为函数式回调接口接收用户传入的具体实现自定义Redis命令操作并在管道模式下进行回调执行action.doInRedis或session.execute。同时管道的打开和关闭connection.openPipeline/connection.closePipeline也支持不同的实现方式如我们熟悉的JedisConnection和Spring Boot 2开始默认使用的LettuceConnection。值得注意的是虽然在Spring框架中存在各类以Template后缀命名的类如RedisTemplate、TransactionTemplate、JdbcTemplate等但是仔细观察可以发现它们实际上使用的并不是模板方法而是回调的方式注3。 public class RedisTemplate K, V extends RedisAccessor implements RedisOperations K, V, BeanClassLoaderAware {// 管道模式命令执行RedisCallbackOverridepublic List Object executePipelined(RedisCallback ? action, Nullable RedisSerializer ? resultSerializer) {return execute((RedisCallback List Object) connection - {connection.openPipeline(); // * 扩展点开启管道模式boolean pipelinedClosed false;try {Object result action.doInRedis(connection); // * 扩展点回调执行用户自定义操作if (result ! null) {throw new InvalidDataAccessApiUsageException(Callback cannot return a non-null value as it gets overwritten by the pipeline);}List Object closePipeline connection.closePipeline(); // * 扩展点关闭管道模式pipelinedClosed true;return deserializeMixedResults(closePipeline, resultSerializer, hashKeySerializer, hashValueSerializer);} finally {if (!pipelinedClosed) {connection.closePipeline();}}});}// 事务管道模式命令执行Overridepublic List Object executePipelined(SessionCallback ? session, Nullable RedisSerializer ? resultSerializer) {// 具体代码省略}} 类似地在数农WMS的库存操作重构中我们定义了ContainerInventoryOperationTemplate「模板类」作为承载库存操作业务逻辑的框架。下述为其中的库存操作核心代码片段。可以看到框架统一定义了库存操作流程并对其中的通用逻辑提供了支持使各类不同的库存操作得以复用如构建库存操作明细、持久化操作明细及同步任务、并发冲突重试等而对于其中随不同库存操作类型变动的逻辑 —— 如操作库存数据、确认前置操作、持久化库存数据等 —— 则通过对ContainerInventoryOperationHandler接口实例的回调实现它们可以被看作是库存操作框架代码中的扩展点。接口由不同类型的库存操作分别实现如库存增加、库存占用、库存转移、库存释放等等。如此如果我们后续需要添加某种新类型的库存操作只需要实现ContainerInventoryOperationHandler接口中定义的个性化逻辑即可而如果我们需要对整个库存操作流程进行迭代也只需要修改ContainerInventoryOperationTemplate中的框架代码而不是像先前那样需要同时修改多处代码这里模板类和库存操作handler的命名均以Container作为前缀是因为数农WMS以容器托盘作为基本的库存管理单元。 Service public class ContainerInventoryOperationTemplate {private Boolean doOperateInTransaction(OperationContext context) {final Boolean transactionSuccess transactionTemplate.execute(transactionStatus - {try {ContainerInventoryOperationHandler handler context.getHandler(); // 库存操作回调handlerhandler.getAndCheckCurrentInventory(context); // 获取并校验库存数据buildInventoryDetail(context); // 构建库存操作明细handler.operateInventory(context); // * 扩展点操作库存数据handler.confirmPreOperationIfNecessary(context); // * 扩展点确认前置操作如库存占用 handler.persistInventoryOperation(context); // * 扩展点持久化库存数据persistInventoryDetailAndSyncTask(context); // 持久化操作明细及同步任务 doSyncOperationIfNecessary(context); // 库存同步操作return Boolean.TRUE;} catch (WhException we) {context.setWhException(we);// 遇到并发冲突异常需要重试if (Objects.equals(we.getErrorCode(), ErrorCodeEnum.CAS_SAVE_ERROR.getCode())) {context.setCanRetry(true);}}// 省略部分代码transactionStatus.setRollbackOnly();return Boolean.FALSE;});// 省略部分代码return transactionSuccess;}} 四 组合与继承 为什么我们选择了基于回调而非模板方法的方式来实现数农WMS的库存操作重构呢由于回调是基于对象之间的组合关系composition实现而模板方法是基于类之间的继承关系inheritance实现我们结合系统实际情况并基于「组合优先于继承」的考量最终选择了使用回调的方式进行代码重构。其原因大致如下 继承打破封装性《Effective Java》在《第18条复合优先于继承》中提到继承是实现代码重用的有力手段但它并非永远是完成这项工作的最佳工具。使用不当会导致软件变得很脆弱。与方法调用不同的是继承打破了封装性。换句话说子类依赖于其超类中特定功能的实现细节。超类的实现有可能会随着发行版本的不同而有所变化如果真的发生了变化子类可能会遭到破坏即使它的代码完全没有改变。同时子类可能继承了定义在父类但其自身并不需要的方法有违最小知识原则Least Knowledge Principle。子类可能因此错误地覆盖并改变了父类中的方法实现导致父类功能的封装性被破坏。而如果我们使用对象间组合的方式则可以避免此类问题的出现。接口优于抽象类仍旧是《Effective Java》在《第20条接口优于抽象类》中提到因为Java只允许单继承所以用抽象类模板方法便是基于抽象类实现作为类型定义受到了限制。而现有的类可以很容易被更新以实现新的接口。接口是定义混合类型mixin的理想选择允许构造非层次结构的类型框架。与之相反的做法是编写一个臃肿的类层次对于每一种要被支持的属性组合都包含一个单独的类。如果整个类型系统中有n个属性那么就必须支持2n种可能的组合这种现象被称为「组合爆炸」即需要定义过多的类。组合替代继承最后王争的《设计模式之美》中提到继承主要有三个作用表示is-a关系支持多态性以及代码复用。而这三个作用都可以通过其他手段达成is-a关系可以通过组合和接口的has-a关系来替代多态性可以利用接口来实现代码复用则可以通过组合和委托来实现。因此从理论上讲通过组合、接口、委托三个技术手段我们可以替换掉继承在项目中不用或者少用复杂的继承关系。这种对象间组合的设计方式比类间继承的方式更加符合开闭原则Open-Closed Principle注4。 结合我们前文中介绍的AbstractQueuedSynchronizer的案例仔细阅读其源码可以发现作者通过代码上的精心设计规避了上文提到的「继承打破封装性」的问题。比如为了不使模板中的骨架逻辑错误地被子类覆盖相关方法如acquire和release均使用了final关键字进行修饰而对于某些必须由子类实现的扩展点在AQS抽象类中均会抛出UnsupportedOperationException异常。然而此处不将扩展点定义为抽象方法而是提供抛出异常的默认实现的原因个人认为是由于AQS中定义了不同形式的获取和释放操作而其锁和同步器的具体实现虽然会继承所有这些方法但依据自身的应用场景往往只关心其中某种版本。比如ReentrantLock中的AQS实现仅关心排他式的版本即tryAcquire和tryRelease而Semaphore中的AQS实现仅关心共享式的版本即tryAcquireShared和tryReleaseShared。解决这类问题的另一种思路便是对这些不同形式的扩展方法进行拆分归置到不同的接口并以回调的方式进行具体功能实现从而避免暴露不必要的方法。 此外AQS内部维护的等待线程队列采用的是基于CLH思想实现的FIFO队列。如果我们同时需要一种优先级队列的内部实现注5并严格按照模板方法的模式对AQS进行扩展则最终可能得到的是一个稍显臃肿的类层次如下图所示 AQS作为JDK的底层并发框架应用场景相对固定且更加侧重性能方面的考虑其扩展性较低无可厚非。而对于如Spring的上层框架在设计时就必须更多地考虑可扩展性的支持。如前文提到的RedisTemplate借助其维护的RedisConnectionFactory即可获得不同类型的底层Redis连接实现而对于其不同形式的管道执行方法管道/事务管道用户只需要实现并传入对应的回调接口RedisCallback/SessionCallback即可而不必感知其不需要的方法定义。这两点便是通过组合委托和回调的方式实现的相较AQS而言显得更加灵活简洁如下图所示 五 再论重构 回到我们的数农WMS库存操作重构虽然ContainerInventoryOperationTemplate与ContainerInventoryOperationHandler之间的关系非常接近策略模式Strategy但由于我们的「模板类」使用Spring的单例模式进行管理其中并没有单独维护某个指定的库存操作handler而是通过方法传参的方式触达它们因此笔者更倾向于使用回调描述两者之间的代码结构。不过读者不必对两者命名的差异过于纠结因为它们的思路是非常相近的。 随着数农WMS代码重构的推进以及对更多库存操作业务场景的覆盖我们不断发现这套重构后的代码框架具备优秀的可扩展性。例如当我们需要为上游系统提供「库存增加并占用」的库存操作原子能力支持时我们发现可以使用组合委托的方式复用「库存增加」和「库存占用」的基本库存操作能力从而简洁高效地完成功能开发。而这点若是单纯基于模板方法的类间继承的方式是无法实现的。具体代码和类图如下 // 库存增加并占用 Component public class IncreaseAndOccupyOperationHandler implements ContainerInventoryOperationHandler {Resourceprivate IncreaseOperationHandler increaseOperationHandler; // 组合「库存增加」操作handlerResourceprivate OccupyOperationHandler occupyOperationHandler; // 组合「库存占用」操作handler// 委托「库存占用」操作handler进行前置操作校验判断是否单据占用已存在Overridepublic void checkPreOperationIfNecessary(ContainerInventoryOperationTemplate.OperationContext context) {occupyOperationHandler.checkPreOperationIfNecessary(context); }// 委托「库存增加」操作handler进行库存信息校验Overridepublic void getAndCheckCurrentInventory(ContainerInventoryOperationTemplate.OperationContext context) {increaseOperationHandler.getAndCheckCurrentInventory(context);}// 委托「库存增加」、「库存占用」操作handler进行「库存增加并占用」操作Overridepublic void operateInventory(ContainerInventoryOperationTemplate.OperationContext context) {increaseOperationHandler.operateInventory(context);occupyOperationHandler.operateInventory(context);}// 其余代码略} 最后无论是基于模板方法还是回调的方式对库存操作进行重构虽然我们可以获得代码复用以及扩展便利的好处但是「模板类」中骨架逻辑的复杂性其实是所有库存操作复杂性的总和个人认为这一点在Spring框架的代码中也有所体现。比如库存增加操作在某些场景下需要在开启数据库事务前获取分布式锁库存占用操作需要判断相关单据是否已经占用了库存等。而模板代码中的骨架逻辑需要为所有这些流程分支提供扩展点从而支持各种类型的库存操作。此外修改模板骨架逻辑的代码时也需要小心谨慎因为一旦模板代码本身出错可能会影响所有的库存操作。这些都对我们代码编写的质量和可维护性提出更高的要求。 六 结语 代码重构并且总结成文的过程要求不断地学习、思辨和实践也让自己获益良多。 注解 对AQS使用了模板方法设计模式的「官方论断」可见于其作者Doug Lea在The java.util.concurrent Synchronizer Framework一文中的论述Class AbstractQueuedSynchronizer ties together the above functionality and serves as a template method pattern base class for synchronizers. Subclasses define only the methods that implement the state inspections and updates that control acquire and release. 此外文中还包含了对等待线程FIFO队列CLH变体、公平性、框架性能等方面的详细讨论。 http://gee.cs.oswego.edu/dl/papers/aqs.pdf参考维基百科Callback词条In object-oriented programming languages without function-valued arguments, such as in Java before its 8 version, callbacks can be simulated by passing an instance of an abstract class or interface, of which the receiver will call one or more methods, while the calling end provides a concrete implementation. Such objects are effectively a bundle of callbacks, plus the data they need to manipulate. They are useful in implementing various design patterns such as Visitor, Observer, and Strategy.https://en.wikipedia.org/wiki/Callback_(computer_programming)Stack Overflow上的某个问答可作为参考I concur - JdbcTemplate isnt an example of template method design pattern. The design pattern used is callback. Note that the goal and effect of both patterns is very similar, the main difference is that template method uses inheritance while callback uses composition (sort of) - by Jiri Tousekh. java - Why is JdbcTemplate an example of the Template method design pattern - Stack Overflow参考维基百科Strategy pattern词条The strategy pattern uses composition instead of inheritance. In the strategy pattern, behaviors are defined as separate interfaces and specific classes that implement these interfaces. This allows better decoupling between the behavior and the class that uses the behavior. The behavior can be changed without breaking the classes that use it, and the classes can switch between behaviors by changing the specific implementation used without requiring any significant code changes. This is compatible with the open/closed principle (OCP), which proposes that classes should be open for extension but closed for modification. https://en.wikipedia.org/wiki/Strategy_patternDoug Lea在The java.util.concurrent Synchronizer Framework中提到The heart of the framework is maintenance of queues of blocked threads, which are restricted here to FIFO queues. Thus, the framework does not support priority-based synchronization.http://gee.cs.oswego.edu/dl/papers/aqs.pdf 参考资料 《设计模式》 设计模式 (豆瓣) The java.util.concurrent Synchronizer Framework http://gee.cs.oswego.edu/dl/papers/aqs.pdf 《Java并发编程实战》 Java并发编程实战 (豆瓣) 维基百科Callback词条 https://en.wikipedia.org/wiki/Callback_(computer_programming) why is jdbctemplate an example of the template method design pattern java - Why is JdbcTemplate an example of the Template method design pattern - Stack Overflow 《Effective Java 3》 Effective Java (豆瓣) 《设计模式之美》 设计模式之美_设计模式_代码重构-极客时间 维基百科Strategy pattern词条 https://en.wikipedia.org/wiki/Strategy_pattern 原文链接 本文为阿里云原创内容未经允许不得转载。
http://www.zqtcl.cn/news/725151/

相关文章:

  • 可信赖的常州网站建设做直播券的网站有多少
  • 网络营销案例分析pptseo策略是什么意思
  • 论坛网站建设视频青岛网站设计软件
  • 租用网站服务器价格清远医院网站建设方案
  • 房地产网站建设方案书福田所有车型
  • 网站功能描述高清视频网络服务器免费
  • 天台做网站微博推广效果怎么样
  • 苏州专门网站网站站长统计怎么做
  • 社交网站开发注意事项call_user_func_array() wordpress
  • 泉州企业免费建站个人网站设计与开发
  • 网站建设流程书籍互联网行业黑话
  • 山亭 网站建设wordpress 添加头像
  • 龙南县建设局网站新手如何做网络推广
  • 网站开发建设赚钱吗巩义旅游网站建设公司
  • 网站建设代码介绍网站顶部导航代码
  • 帮别人做网站需要什么能力sem专员
  • 无锡网站建设 app推广软件
  • 免费入驻的外贸网站网站建设怎么打开
  • 怎么做中英文网站网站建设费做什么
  • 信阳网站建设汉狮怎么样做曖視頻网站
  • 做电影电视剧网站推广移动应用开发是什么意思
  • 网站排名优化策划中山搜索引擎优化
  • 网站建设培训证书平台型网站建设预算表
  • 网站建设后压缩代码网站如何做进一步优化
  • 大型旅游网站源码 织梦襄阳网站建设楚翼网络
  • 快速搭建网站服务器做历史卷子的网站
  • 淘口令微信网站怎么做通化seo招聘
  • 帮人做传销网站违法吗深圳也放开了
  • 发布程序后网站有很多促销策略
  • 网页网站项目综合网站建设合同.doc