这是我最后要写的设计模式,和一些例子。请首先查看本文的第1部分“ 使用 Java 的 GoF 设计模式(第 1 部分) ”。
为了理解四人组的设计模式原理和历史,我做了一个 10 分钟的短视频。 (作为 PluralSight 作者试录的)。
视频: https://youtu.be/vq9zkZBjWkw
我想出了一些例子来帮助理解“设计模式”。试着下载代码,看看它是否能帮助你更好地理解这些。您可以给这篇文章添加书签,方便以后快速参考。
如果您需要在欧洲国家使用亚洲吹风机,因为每个都有不同的插座类型,你会怎么做?我会寻找一个适配器!在现实生活中,当我们想要插入和播放类似但不兼容的接口,我们使用 适配器 。适配器通过组成适配对象并继承所需的接口或通过多重继承来适配期望的接口。
附带的例子是真实世界计算机的方案,这里我想要插入一个扩展的硬件驱动(USB 时代之前),一个 SeagateDrive 的接口类型是 SeagateGeneric 的,这对于一个 SamsungComputer,SeagateGeneric 提供了 read() 和 write() 方法来实现目的,它的内部需要适配实际的 bufferData(),flushData() 和 purgeData() 方法给 Computer。需要注意的是,这里没有等价的 purgeData() 方法。在这个方案里,一个办法就是当硬件驱动无论什么时候调用时,都会抛出一个异常,而在真实世界中,它就是这么做的。在这个方案中,SeagateAdapter 转换了适配器的执行,实现了 Computer 中的接口。它将一个 SeagateGeneric 实例封装在内部进行参照并适配到 Computer 接口,这在实际上需要在 SeagateGeneric 内实现与 Computer 标准匹配的三个 read() 调用。三种转换都是通过被适配者适配的。
在下面的代码片段中提供了核心概念。你还可以下载完整的 示例代码 。
package com.sumsoft.design.patterns.adapter; /* * @author Sumith Puri */ public interface Computer { public void flushData(); public void bufferData(); public void purgeData(); }
package com.sumsoft.design.patterns.adapter; /* * @author Sumith Puri */ public interface SeagateGeneric { public void read(); public void write(); }
package com.sumsoft.design.patterns.adapter; /* * @author Sumith Puri */ public class SeagateDrive implements SeagateGeneric { @Override public void read() { System.out.println("Reading @ 7200 RPM from Seagate B Series"); } @Override public void write() { System.out.println("Writing @ 1 Mbps to Seagate B Series"); } }
package com.sumsoft.design.patterns.adapter; /* * @author Sumith Puri */ public class SeagateAdapter implements Computer { SeagateGeneric seagateGenericDrive; public SeagateAdapter(SeagateGeneric seagateGenericDrive) { this.seagateGenericDrive = seagateGenericDrive; } @Override public void bufferData() { seagateGenericDrive.read(); seagateGenericDrive.read(); seagateGenericDrive.read(); } @Override public void flushData() { seagateGenericDrive.write(); seagateGenericDrive.write(); } @Override public void purgeData() { System.out.println("Operation Not Supported: Seagate Drive cannot Purge Data..."); } }
PCAssembler是这里的主类。你可以尝试把你自己的设备适配到计算机。
考虑一个场景,其中为了实现所需的功能我们需要调用不同类的多个方法。此外,请考虑此功能在你的代码中会反复使用。如果你正在考虑一个备选方案,其中你可以执行直接调用,你必然会遇到代码维护的问题以紧耦合的代码。如果这些是远程调用,它在性能方面将会更糟。这就是 facade 起作用的地方,其中多个方法调用被封装到 facade 类的单个方法中以实现期望的功能。相比于单独实现的方案,它为我们提供了单点变化和松耦合。像如 SessionFacade(EJB)这种远程方法调用模式据此改写,以提高整体性能和降低复杂度。
附加的示例是一个 InvoiceManagerFacade 的非常简单的应用场景,它具有 addInvoice() 和 deleteInvoice() 方法。为了实现期望的结果,这些方法中的每一个都封装来自 OrderManager、LedgerManager 和 BillingManager 类方法的调用。
package com.sumsoft.design.patterns.facade; /* * @author Sumith Puri */ public interface InvoiceSessionFacade { public void addInvoice(Invoice invoice); public void deleteInvoice(Invoice invoice); }
package com.sumsoft.design.patterns.facade; /* * @author Sumith Puri */ public class InvoiceSessionFacadeImpl implements InvoiceSessionFacade { // This is only for the example, Do not follow // this kind of initialisation in your code OrderManager orderManager = new OrderManager(); LedgerManager ledgerManager = new LedgerManager(); BillingManager billingManager = new BillingManager(); @Override public void addInvoice(Invoice invoice) { orderManager.initOnInvoice(invoice.getInvoiceId()); ledgerManager.initOnInvoice(invoice.getInvoiceId()); billingManager.initOnInvoice(invoice.getInvoiceId()); } @Override public void deleteInvoice(Invoice invoice) { orderManager.purgeInvoice(invoice.getInvoiceId()); ledgerManager.cascadeDeleteInvoice(invoice.getInvoiceId()); billingManager.deleteInvoice(invoice.getInvoiceId()); } }
AccountsCentral 是主类。试着将你自己的方法添加到 facade 类中,或者试着创建一个新的 face 类型。
想象一个现实世界的情况,这个工厂中正在创建铝钉和螺钉。虽然机器必须通过类似的过程创建它们,但是每个步骤被实现的方式可能不同。当我们在软件中考虑这样的情况时,我们使用 模板模式 。模板模式定义了重新使用具有不同或稍微不同输出的各种实现算法的方式。
在附加的示例中,抽象类 SoftwareProcessor 为 deliverSoftware() 定义了一组通用的算法步骤(函数)。这个类就是我的模板类。由于基于所使用的技术栈的工程的实现和测试阶段不同, CProcessor 和 JavaProcessor 类会针对这些步骤调整此算法。通用方法都在 SoftwareProcessor 中实现,具体的方法是抽象的。
只有核心概念在下面的代码段中提供。您可以在此下载完整工程/应用程序的 示例代码 。
package com.sumsoft.design.patterns.template; /* * @author Sumith Puri */ abstract class SoftwareProcessor { public void deliverSoftware() { requirementsClarification(); functionalSpecification(); technicalSpecification(); implementModules(); testModules(); if (!isPlatformIndependent()) platformTest(); supportPhase(); } public void requirementsClarification() { System.out.println("Default Requirements Clarification"); } public void functionalSpecification() { System.out.println("Default Functional Specification"); } public void technicalSpecification() { System.out.println("Default Technical Specification"); } public abstract void implementModules(); public abstract void testModules(); public boolean isPlatformIndependent() { return false; } public abstract void platformTest(); public void supportPhase() { System.out.println("Default Support Contract"); } }
package com.sumsoft.design.patterns.template; /* * @author Sumith Puri */ public class SoftwareConsultants { public static void main(String args[]) { SoftwareProcessor softwareProcessor01 = new CProcessor(); softwareProcessor01.deliverSoftware(); SoftwareProcessor softwareProcessor02 = new JavaProcessor(); softwareProcessor02.deliverSoftware(); } }
SoftwareConsultants 用于运行这个示例。试试添加下你自己的 processor。
为了满足拥有一个能处理元素集合而不用暴露其内部实现的句柄之需要,就要用到 迭代器模式(terator Pattern ) 。我将此称为纯粹的编程模式。通过利用此句柄(迭代器 Iterator), 使用了集合对象的客户端能够很容易的处理类型相同的元素,而无需依赖于内部逻辑。
在示例中,ProductMenu 持有 ProductItem 的目录或者清单。这份清单及其使用方式在实现上应该同客户端保持无关。因此,就有了对于实现了通用迭代器 Iterator 接口的 ProductIterator 需要。ProductMenu 的 createIterator() 方法会将 ProductItem 的数组实现传递给 ProductIterator 的构造器。
下面的代码片段只提供了核心的概念。你可以 下载 到完整的代码/应用程序。
package com.sumsoft.design.patterns.iterator; /* * @author Sumith Puri */ public interface Iterator { public Object next(); public boolean hasNext(); }
package com.sumsoft.design.patterns.iterator; /* * @author Sumith Puri */ public class ProductIterator implements Iterator { ProductItem[] productItems; int marker = 0; public ProductIterator(ProductItem[] productItems) { this.productItems = productItems; } @Override public boolean hasNext() { boolean hasNext = false; if (marker < productItems.length) hasNext = true; return hasNext; } @Override public Object next() { ProductItem productItem = null; if (marker < productItems.length) productItem = productItems[marker++]; return productItem; } }
package com.sumsoft.design.patterns.iterator; /* * @author Sumith Puri */ public class ProductMenu { ProductItem[] productItems; int maxSize = 0; public ProductMenu(int size) { maxSize = size; productItems = new ProductItem[size]; } public ProductIterator createIterator() { return new ProductIterator(productItems); } public void addReplaceItem(int index, ProductItem productItem) { if (index > maxSize) System.out.println("Cannot Add as Index Increases MaxSize"); else productItems[index] = productItem; } }
该示例可以使用 ProductMenuTester 运行。
状态模式( State Pattern )定义了一种方法,该方法可以用来维护同一台机器或者同一个类的各种步骤或状态。机器这个词很容易理解,因为它是一个真实世界场景中最简单的例子,它里面就需要按步骤或者设置状态来操作同一个对象的需要— 一个步骤向下一个状态的过渡则是由一个(或者多个)动作来定义的。
附上一个简单示例帮助理解 — 这是一个在线购物网站,叫做 OnlineShopping。该站点的限制就是给定一个任意的时间点,只可以购买和处理一项商品。在购买和处理期间的不同状态有选定(SelectionState), 订购(PurchaseState), 核准(AuthoriseState), 包装(AssembleState) (可选),以及派送(DispatchState)。这些状态中的每一个都是相跟着按顺序进行处理的。OnlineShopping 为每个状态维护一个实例变量 — 还有一个 currentState 变量。OnlineShopping 里面存在着不同的状态操作方法,它们是 selection(), purchase(), authorise(), assemble(), 以及 dispatch()。当一个客户端调用到这些方法时,实际的调用是由 currentState 变量中管理的状态实现来执行的。所有的状态实现逻辑都实现了 State 接口,其中指定了生命周期方法。
下面的代码片段中只提供了核心概念,你可以 下载 示例的完整代码/应用程序。
package com.sumsoft.design.patterns.state; /* * @author Sumith Puri */ public interface State { public void purchase(); public void authorise(); public void assemble(); public void dispatch(); public void complete(); }
package com.sumsoft.design.patterns.state; /* * @author Sumith Puri */ public class SelectionState implements State { OnlineShopping shopping; public SelectionState(OnlineShopping shopping) { this.shopping = shopping; } @Override public void assemble() { System.out.println("Cannot Assemble Unless Selected"); } @Override public void authorise() { System.out.println("Cannot Authorise Unless Selected"); } @Override public void dispatch() { System.out.println("Cannot Dispatch Unless Selected"); } @Override public void purchase() { System.out.print("-> Purchase"); shopping.setCurrentState(shopping.getPurchaseState()); } @Override public void complete() { System.out.println("-> Complete"); shopping.setCurrentState(shopping.getSelectionState()); } }
package com.sumsoft.design.patterns.state; /* * @author Sumith Puri */ public class OnlineShopping { State currentState; SelectionState selectionState; PurchaseState purchaseState; AuthoriseState authoriseState; AssembleState assembleState; DispatchState dispatchState; public OnlineShopping() { selectionState = new SelectionState(this); purchaseState = new PurchaseState(this); authoriseState = new AuthoriseState(this); assembleState = new AssembleState(this); dispatchState = new DispatchState(this); currentState = selectionState; } /** * @return the currentState */ public synchronized State getCurrentState() { return currentState; } /** * @param currentState the currentState to set */ public synchronized void setCurrentState(State currentState) { this.currentState = currentState; } /** * @return the selectionState */ public synchronized SelectionState getSelectionState() { return selectionState; } /** * @param selectionState the selectionState to set */ public synchronized void setSelectionState(SelectionState selectionState) { this.selectionState = selectionState; } /** * @return the purchaseState */ public synchronized PurchaseState getPurchaseState() { return purchaseState; } /** * @param purchaseState the purchaseState to set */ public synchronized void setPurchaseState(PurchaseState purchaseState) { this.purchaseState = purchaseState; } /** * @return the authoriseState */ public synchronized AuthoriseState getAuthoriseState() { return authoriseState; } /** * @param authoriseState the authoriseState to set */ public synchronized void setAuthoriseState(AuthoriseState authoriseState) { this.authoriseState = authoriseState; } /** * @return the assembleState */ public synchronized AssembleState getAssembleState() { return assembleState; } /** * @param assembleState the assembleState to set */ public synchronized void setAssembleState(AssembleState assembleState) { this.assembleState = assembleState; } /** * @return the dispatchState */ public synchronized DispatchState getDispatchState() { return dispatchState; } /** * @param dispatchState the dispatchState to set */ public synchronized void setDispatchState(DispatchState dispatchState) { this.dispatchState = dispatchState; } public void purchase(String itemName) { System.out.print(itemName); currentState.purchase(); } public void authorise() { currentState.authorise(); } public void assemble() { currentState.assemble(); } public void dispatch() { currentState.dispatch(); } public void complete() { currentState.complete(); } }
ShoppingClient是主类。你可以尝试加入你自己的状态,并在里面带上必须要的生命周期方法。
注意: 上述代码片段只解释了各种设计模式的核心概念。你可通过上面提供的链接下载到完整代码并在你的系统上跑起来,以获得更完整的理解。你也可以在你自己的示例中对这些代码进行修改,以巩固理解。