├── .gitignore ├── README.md ├── abstract-factory ├── README.md ├── images │ ├── abstract-factory.png │ └── products.png ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatterns │ └── abstractfactory │ ├── AndroidCable.java │ ├── Cable.java │ ├── Charger.java │ ├── Client.java │ ├── IPhone.java │ ├── IPhoneCable.java │ ├── IPhoneCharger.java │ ├── IPhoneFactory.java │ ├── Phone.java │ ├── PhoneFactory.java │ ├── XiaomiCharger.java │ ├── XiaomiFactory.java │ └── XiaomiPhone.java ├── adaptor ├── README.md ├── images │ ├── class-adaptor.png │ └── object-adaptor.png ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatterns │ └── adaptor │ ├── AdvancedComputer.java │ ├── Computer.java │ ├── DisplayAdaptor.java │ ├── DisplayRequire.java │ └── Projector.java ├── bridge ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatterns │ └── bridge │ ├── Client.java │ ├── Enchantment.java │ ├── Hammer.java │ ├── HolyEnchantment.java │ ├── ShadowEnchantment.java │ ├── Wand.java │ └── Weapon.java ├── builder ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatterns │ └── builder │ ├── Armor.java │ ├── Client.java │ ├── HairColor.java │ ├── HairType.java │ ├── Hero.java │ ├── Profession.java │ └── Weapon.java ├── chainofresponsibility ├── README.md ├── images │ ├── chain_pattern_uml_diagram.jpg │ ├── chainofresponsibility-example.png │ └── chainofresponsibility.png ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatterns │ └── chainofresponsibility │ ├── AbstractLogger.java │ ├── Client.java │ ├── ConsoleLogger.java │ ├── ErrorLogger.java │ └── FileLogger.java ├── command ├── README.md ├── images │ ├── after-decoupling.png │ └── before-decoupling.png ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatterns │ └── command │ ├── Client.java │ ├── ColorFiller.java │ ├── ColorFilling.java │ ├── Command.java │ ├── DrawingApp.java │ ├── ShapeDrawer.java │ └── ShapeDrawing.java ├── composite ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatterns │ └── composite │ ├── Client.java │ ├── Directory.java │ ├── Entry.java │ ├── EntryAddException.java │ └── File.java ├── decorator ├── README.md ├── images │ ├── decorator.png │ └── result.png ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatterns │ └── decorator │ ├── BoardStringDisplay.java │ ├── Client.java │ ├── Display.java │ ├── FullBoardStringDisplay.java │ ├── SideBoardStringDisplay.java │ └── StringDisplay.java ├── facade ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatterns │ └── facade │ ├── Circle.java │ ├── Client.java │ ├── Rectangle.java │ ├── Shape.java │ ├── ShapeDrawer.java │ └── Triangle.java ├── factory-method ├── README.md ├── images │ ├── factorymethod.png │ └── hiera-factory-method.png ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatterns │ ├── factorymethod1 │ ├── Circle.java │ ├── CircleFactory.java │ ├── Client.java │ ├── Rectangle.java │ ├── RectangleFactory.java │ ├── Shape.java │ ├── ShapeFactory.java │ ├── Triangle.java │ └── TriangleFactory.java │ └── hierafactorymethod │ ├── AbstractFactory.java │ ├── AbstractProduct.java │ ├── AbstractSubFactory.java │ ├── AbstractSubProduct.java │ ├── Client.java │ ├── ConcreteFactory.java │ ├── ConcreteProduct.java │ ├── ConcreteSubFactory.java │ └── ConcreteSubProduct.java ├── flyweight ├── README.md ├── images │ ├── flyweight-font.png │ ├── flyweight-lifeexample.jpg │ └── flyweight-output.png ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── getset │ │ └── designpatterns │ │ └── flyweight │ │ ├── Client.java │ │ ├── NumSeal.java │ │ ├── NumSealFactory.java │ │ └── NumsPrinter.java │ └── resources │ └── fonts │ ├── seal-0.txt │ ├── seal-1.txt │ ├── seal-2.txt │ ├── seal-3.txt │ ├── seal-4.txt │ ├── seal-5.txt │ ├── seal-6.txt │ ├── seal-7.txt │ ├── seal-8.txt │ └── seal-9.txt ├── interpreter ├── README.md ├── images │ ├── express-tree.png │ └── iterpreter.png ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatterns │ └── interpreter │ ├── Add.java │ ├── Client.java │ ├── Div.java │ ├── Expression.java │ ├── Mul.java │ ├── Num.java │ └── Sub.java ├── iterator ├── README.md ├── images │ ├── Collection_interfaces.png │ └── iterator-diagram.png ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatters │ └── iterator │ ├── ArrayList.java │ ├── Client.java │ ├── Iterable.java │ └── Iterator.java ├── mediator ├── README.md ├── images │ ├── mediator-example1.png │ ├── mediator-example2.png │ └── mediator-example3.png ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatterns │ └── mediator │ ├── Client.java │ ├── Developer.java │ ├── Operator.java │ ├── TeamMember.java │ ├── TechLeader.java │ └── Tester.java ├── memento ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatterns │ └── memento │ ├── Snapshot.java │ ├── User.java │ └── VirtualMachine.java ├── observer ├── README.md ├── images │ ├── Java-Observable.png │ ├── Java-observer.png │ └── observer.png ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatterns │ └── observer │ ├── Client.java │ ├── Publisher.java │ ├── Subscriber.java │ ├── WeixinAccount.java │ └── WeixinPublisher.java ├── pom.xml ├── prototype ├── ABOUT_CLONE.md ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatterns │ ├── clonetest │ ├── ClassA.java │ ├── ClassB.java │ ├── Client.java │ ├── EarTag.java │ └── Sheep.java │ └── shape │ ├── Circle.java │ ├── Client.java │ ├── Rectangle.java │ ├── Shape.java │ └── Triangle.java ├── proxy ├── README.md ├── images │ ├── adaptor-pattern.png │ ├── decorator-pattern.png │ ├── http-proxy.png │ ├── http_proxy.png │ └── proxy-pattern.png ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatterns │ └── proxy │ ├── httpproxy │ ├── Client.java │ ├── HttpProxy.java │ ├── HttpProxyHandler.java │ ├── HttpServer.java │ └── OverseasHttpServer.java │ └── illustration │ ├── Client.java │ ├── ProxySubject.java │ ├── RealSubject.java │ └── Subject.java ├── simple-factory ├── README.md ├── images │ ├── simplefactory1.png │ └── simplefactory2.png ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatterns │ ├── simplefactory1 │ ├── Circle.java │ ├── Client.java │ ├── Rectangle.java │ ├── Shape.java │ ├── ShapeFactory.java │ └── Triangle.java │ └── simplefactory2 │ ├── American.java │ ├── Chinese.java │ ├── Client.java │ ├── Person.java │ └── PersonFactory.java ├── singleton ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatterns │ └── singleton │ ├── Client.java │ ├── EnumSingleton.java │ ├── LazyHandlerSingleObject.java │ ├── LazySingleObject.java │ └── SingleObject.java ├── state ├── README.md ├── images │ └── state.png ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatterns │ └── state │ ├── Client.java │ ├── Developer.java │ ├── EffectiveState.java │ ├── ExhaustedState.java │ ├── State.java │ └── TianRenHeYiState.java ├── strategy ├── README.md ├── images │ └── strategy.png ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatterns │ └── strategy │ ├── AddOperation.java │ ├── Calculator.java │ ├── Client.java │ ├── DivOperation.java │ ├── MulOperation.java │ ├── Operation.java │ └── SubOperation.java ├── template-method ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── getset │ └── designpatterns │ └── templatemethod │ ├── Client.java │ ├── SchoolPerson.java │ ├── Student.java │ └── Teacher.java └── visitor ├── README.md ├── pom.xml └── src └── main └── java └── com └── getset └── designpatterns └── visitor ├── Area.java ├── Calculator.java ├── Circle.java ├── Client.java ├── Perimeter.java ├── Rectangle.java ├── Shape.java ├── Square.java └── Triangle.java /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .metadata 3 | .settings 4 | .classpath 5 | .project 6 | *.class 7 | *.jar 8 | *.war 9 | *.ear 10 | .idea 11 | *.iml 12 | *.swp 13 | /bin/ 14 | *.log 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 为了自己能够系统有效地学习设计模式,也希望能够帮助到其他想对设计模式有了解的同学,我边学习边做了这个系列的设计模式笔记。 3 | 4 | 本系列主要由Java语言来实现。内容来自国外网站[Java-desing-patterns](http://java-design-patterns.com/patterns/),并结合了其他相关内容,如阎宏博士的《JAVA与模式》一书以及[设计模式|菜鸟教程](http://www.runoob.com/design-pattern/design-pattern-intro.html)等资料。所以—— 5 | 6 | * 如果你在找一个详细的、靠谱的、认真的设计模式系列文档, 7 | * 如果你觉得设计模式的学习总是虎头蛇尾、难以坚持, 8 | * 如果你觉得许多文档只是为了设计模式而设计,并非从问题或需求出发,过后很难学以致用, 9 | * …… 10 | 11 | 那么,希望这个系列的笔记能够帮到你。我们一起努力! 12 | 13 | 14 | # 设计模式简介 15 | 16 | 设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。 17 | 18 | 设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理地运用设计模式可以完美地解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式都描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是设计模式能被广泛应用的原因。 19 | 20 | ## Gang of Four 21 | 说到设计模式,就不能不提到大名鼎鼎的“四人帮”(Gang of Four)。在 1994 年,由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 四人合著出版了一本名为 **Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素)** 的书,该书首次提到了软件开发中设计模式的概念。 22 | 四位作者合称 GOF(四人帮,全拼 Gang of Four)。他们所提出的设计模式主要是基于以下的面向对象设计原则: 23 | 24 | * 对接口编程而不是对实现编程。 25 | * 优先使用对象组合而不是继承。 26 | 27 | ## And more ... 28 | 除了Gang of Four提到的创建型、结构型、行为型的三大类共23个设计模式,还会涉及到JavaEE、Spring、I/O与性能、业务层与持久层等不同方面所涉及到的一些典型的设计模式,总数达百余个。设计模式是内功中的精华,技多不压身,多研究一些总是没有坏处的,不是吗? 29 | 30 | # 设计模式六大原则 31 | 32 | 1. 开闭原则(Open Close Principle) 33 | 开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。 34 | 2. 里氏代换原则(Liskov Substitution Principle) 35 | 里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。 36 | 3. 依赖倒转原则(Dependence Inversion Principle) 37 | 这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。 38 | 4. 接口隔离原则(Interface Segregation Principle) 39 | 这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。 40 | 5. 迪米特法则,又称最少知道原则(Demeter Principle) 41 | 最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。 42 | 6. 合成复用原则(Composite Reuse Principle) 43 | 合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。 44 | 45 | # 例子 46 | 1. [简单工厂模式](simple-factory) 47 | 2. [工厂方法模式](factory-method) 48 | 3. [抽象工厂模式](abstract-factory) 49 | 4. [单例模式](singleton) 50 | 5. [建造者模式](builder) 51 | 6. [原型模式](prototype) 52 | 7. [适配器模式](adaptor) 53 | 8. [桥接模式](bridge) 54 | 9. [代理模式](proxy) 55 | 10. [装饰器模式](decorator) 56 | 11. [合成模式](composite) 57 | 12. [观察者模式](observer) 58 | 13. [门面模式](facade) 59 | 14. [策略模式](strategy) 60 | 15. [模板方法模式](template-method) 61 | 16. [命令模式](command) 62 | 17. [责任链模式](chainofresponsibility) 63 | 18. [享元模式](flyweight) 64 | 19. [迭代器模式](iterator) 65 | 20. [解释器模式](interpreter) 66 | 21. [调停者模式](mediator) 67 | 22. [状态模式](state) 68 | 23. [备忘录模式](memento) 69 | 24. [访问者模式](visitor) -------------------------------------------------------------------------------- /abstract-factory/README.md: -------------------------------------------------------------------------------- 1 | 抽象工厂模式是在 [工厂方法模式](../factory-method) 之上的有一次升级,以便能够处理更加复杂的对象创建场景。因此也是所有形态的工厂模式中最为抽象和最具一般性的一种形态。 2 | 3 | 有朋友可能会想了, [工厂方法模式](../factory-method) 已经是对 [简单工厂模式](../simple-factory) 的具体工厂类做了抽象了(增加了抽象工厂),那抽象工厂模式的“抽象”是什么意思呢?用来处理什么问题呢? 4 | 5 | 其实无论是 [简单工厂模式](../simple-factory) ,还是 [工厂方法模式](../factory-method) ,在涉及到“产品组装”的时候就变得吃力了。比如大家买手机的时候,除了手机本身,还有充电器、数据线等。苹果手机只能使用苹果的充电线,Android手机也有不同型号的充电线,充电器也需要根据手机选择不同的功率适配的型号。 6 | 7 | 如果还使用 [简单工厂模式](../simple-factory) 或者 [工厂方法模式](../factory-method) ,那么只能将手机、充电器和数据线看作一个产品给出,这还勉强能hold住。如果涉及到更加复杂的情况,比如组装电脑,从CPU、主板、内存、显卡到机箱、电源等等,各种品牌和型号,还有各个组件的兼容性,其可能的组合成千上万,每一个组合都实现一个具体的工厂类,想想都要呵呵了。 8 | 9 | 所以,对于“组装”或有“产品族“概念的产品,需要进一步进行抽象。 10 | 11 | # 例子 12 | 13 | 多说无益,还是以刚才手机的例子来动手写写代码看看。 14 | 15 | 假设小明开了一家手机店,主营两款产品,iPhone和小米手机。我们知道这两种手机的数据线和充电器是不能混用的。 16 | 17 | ![手机及组件](images/products.png) 18 | 19 | 还是图比较清楚,就不贴代码了,源码见本文开头给出的地址。无论是`use()`、`charge()`还是`transmit()`其实都是打印一句话,说明是什么手机、充电器和数据线。 20 | 21 | 无论是小米手机还是苹果手机,亦或是其他手机,消费者拿到手的都是一个盒子,里边包含手机、充电器和数据线。那么做手机的工厂应该至少具备生产手机、充电器和数据线的能力,既然是“能力”,那么就用接口或抽象类来约束。 22 | 23 | PhoneFactory.java 24 | 25 | public interface PhoneFactory { 26 | Phone producePhone(); 27 | Cable produceCable(); 28 | Charger produceCharger(); 29 | } 30 | 31 | 那么对于苹果和小米工厂来说,具体生产时就实现这个接口的方法就可以了,同时实现方法的时候,也保证了自己生产的数据线和手机是适配的。 32 | 33 | IPhoneFactory.java 34 | 35 | public class IPhoneFactory implements PhoneFactory { 36 | public Phone producePhone() { 37 | return new IPhone(); 38 | } 39 | 40 | public Cable produceCable() { 41 | return new IPhoneCable(); 42 | } 43 | 44 | public Charger produceCharger() { 45 | return new IPhoneCharger(); 46 | } 47 | } 48 | 49 | XiaomiFactory.java 50 | 51 | public class XiaomiFactory implements PhoneFactory { 52 | public Phone producePhone() { 53 | return new XiaomiPhone(); 54 | } 55 | 56 | public Cable produceCable() { 57 | return new AndroidCable(); 58 | } 59 | 60 | public Charger produceCharger() { 61 | return new XiaomiCharger(); 62 | } 63 | } 64 | 65 | 66 | 具体在使用这个设计模式的时候,就很灵活了,比如最简单的: 67 | 68 | Client.java 69 | 70 | public class Client { 71 | public static void main(String[] args) { 72 | PhoneFactory factory = new IPhoneFactory(); 73 | factory.produceCable().transmit(); 74 | factory.produceCharger().charge(); 75 | factory.producePhone().use(); 76 | } 77 | } 78 | 79 | 产品组件是由具体工厂决定的,使用小米工厂生产的就是小米的手机、数据线和充电器,苹果工厂一样道理。不过这个`Client`实在太过简单,具体的场景中如果使用,It's your call ! 抽象工厂设计模式的要诀在于“抽象”,具体往下看。 80 | 81 | # 总结 82 | 83 | 这时候我们来看一下类关系图(先把具体产品类抛在一边): 84 | 85 | ![abstract-factory](images/abstract-factory.png) 86 | 87 | 这里我们一下就明白了为什么这种模式叫做“抽象工厂”模式,相对于 [简单工厂模式](../simple-factory) 和 [工厂方法模式](../factory-method) 来说,抽象工厂的作用是将“抽象组件”组合成“抽象产品”。所以我们看到,`PhoneFactory`的产出是一个一个的接口。所以,** 抽象工厂并不关心具体组件的实现,而是只关心接口**。 88 | 89 | “抽象工厂模式”的“抽象”并非是说有抽象的工厂接口,而是说工厂的产出面向的是抽象的产品。 90 | 91 | 我们再来看一下“开闭原则”的支持: 92 | 1. 当增加新的产品族时,比如增加OPPO和VIVO的手机产品,现有的代码均不需要修改,所以支持对扩展开放和对修改关闭; 93 | 2. 但是当产品族中增加组件的时候,比如国家规定所有的手机类产品均需要附加一片屏幕保护膜,那么抽象工厂接口和所有的具体工厂类均需要修改,所以并不能完全满足”开闭原则“的要求。 -------------------------------------------------------------------------------- /abstract-factory/images/abstract-factory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/abstract-factory/images/abstract-factory.png -------------------------------------------------------------------------------- /abstract-factory/images/products.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/abstract-factory/images/products.png -------------------------------------------------------------------------------- /abstract-factory/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 4.0.0 7 | abstract-factory 8 | 9 | 10 | com.getset.designpatterns 11 | get-designpatterns 12 | 1.0-SNAPSHOT 13 | ../ 14 | 15 | 16 | -------------------------------------------------------------------------------- /abstract-factory/src/main/java/com/getset/designpatterns/abstractfactory/AndroidCable.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.abstractfactory; 2 | 3 | public class AndroidCable implements Cable { 4 | public void transmit() { 5 | System.out.println("Transmit data or power with Android Cable. "); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /abstract-factory/src/main/java/com/getset/designpatterns/abstractfactory/Cable.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.abstractfactory; 2 | 3 | public interface Cable { 4 | void transmit(); 5 | } 6 | -------------------------------------------------------------------------------- /abstract-factory/src/main/java/com/getset/designpatterns/abstractfactory/Charger.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.abstractfactory; 2 | 3 | public interface Charger { 4 | void charge(); 5 | } 6 | -------------------------------------------------------------------------------- /abstract-factory/src/main/java/com/getset/designpatterns/abstractfactory/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.abstractfactory; 2 | 3 | public class Client { 4 | public static void main(String[] args) { 5 | PhoneFactory factory = new IPhoneFactory(); 6 | factory.produceCable().transmit(); 7 | factory.produceCharger().charge(); 8 | factory.producePhone().use(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /abstract-factory/src/main/java/com/getset/designpatterns/abstractfactory/IPhone.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.abstractfactory; 2 | 3 | public class IPhone implements Phone { 4 | public void use() { 5 | System.out.println("Use iPhone. "); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /abstract-factory/src/main/java/com/getset/designpatterns/abstractfactory/IPhoneCable.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.abstractfactory; 2 | 3 | public class IPhoneCable implements Cable { 4 | public void transmit() { 5 | System.out.println("Transmit data or power with iPhone Cable. "); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /abstract-factory/src/main/java/com/getset/designpatterns/abstractfactory/IPhoneCharger.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.abstractfactory; 2 | 3 | public class IPhoneCharger implements Charger { 4 | public void charge() { 5 | System.out.println("Charge with iPhone charger. "); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /abstract-factory/src/main/java/com/getset/designpatterns/abstractfactory/IPhoneFactory.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.abstractfactory; 2 | 3 | public class IPhoneFactory implements PhoneFactory { 4 | public Phone producePhone() { 5 | return new IPhone(); 6 | } 7 | 8 | public Cable produceCable() { 9 | return new IPhoneCable(); 10 | } 11 | 12 | public Charger produceCharger() { 13 | return new IPhoneCharger(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /abstract-factory/src/main/java/com/getset/designpatterns/abstractfactory/Phone.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.abstractfactory; 2 | 3 | public interface Phone { 4 | void use(); 5 | } 6 | -------------------------------------------------------------------------------- /abstract-factory/src/main/java/com/getset/designpatterns/abstractfactory/PhoneFactory.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.abstractfactory; 2 | 3 | public interface PhoneFactory { 4 | Phone producePhone(); 5 | Cable produceCable(); 6 | Charger produceCharger(); 7 | } 8 | -------------------------------------------------------------------------------- /abstract-factory/src/main/java/com/getset/designpatterns/abstractfactory/XiaomiCharger.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.abstractfactory; 2 | 3 | public class XiaomiCharger implements Charger { 4 | public void charge() { 5 | System.out.println("Charge with Xiaomi Charger. "); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /abstract-factory/src/main/java/com/getset/designpatterns/abstractfactory/XiaomiFactory.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.abstractfactory; 2 | 3 | public class XiaomiFactory implements PhoneFactory { 4 | public Phone producePhone() { 5 | return new XiaomiPhone(); 6 | } 7 | 8 | public Cable produceCable() { 9 | return new AndroidCable(); 10 | } 11 | 12 | public Charger produceCharger() { 13 | return new XiaomiCharger(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /abstract-factory/src/main/java/com/getset/designpatterns/abstractfactory/XiaomiPhone.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.abstractfactory; 2 | 3 | public class XiaomiPhone implements Phone { 4 | public void use() { 5 | System.out.println("Use Xiaomi. "); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /adaptor/README.md: -------------------------------------------------------------------------------- 1 | 适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。 2 | 3 | “适配器”这个词我们平时倒是不少见,比如“电源适配器”,将220V的点转换为电子设备可以接收的小电压的电,可见变压器就是一种“适配器”。不难想到,读卡器是作为内存卡和笔记本之间的适配器,您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。还有我们平时用到的转接头,也是适配器,一端是VGA接口的信号,另一端就可以转换成HDMI的信号。 4 | 5 | 有时候,这种设计模式也被称为包装(Wrapper)模式。我们平时经常会写或遇到wrapper类,就是同样的作用。 6 | 7 | # 例子 8 | 9 | 就以刚才提到的转接头为例。 10 | 11 | 今天对小明来说是个重要的日子,因为今天他要面临毕业答辩了。他已经做好了答辩PPT,但是不确定答辩现场的投影仪是VGA接口还是HDMI接口。为了确保万无一失,必须做好两手准备,无论是VGA还是HDMI都要能显示。 12 | 13 | DisplayRequire.java 14 | 15 | public interface DisplayRequire { 16 | String transmitWithVGA(); 17 | String transmitWithHDMI(); 18 | } 19 | 20 | 现在PPT在自己的老旧笔记本上,这台笔记本只有一个VGA接口。 21 | 22 | Computer.java 23 | 24 | public class Computer { 25 | protected String ppt = "My PPT report"; 26 | public String transmitWithVGA() { 27 | return ppt; 28 | } 29 | } 30 | 31 | 摆在他面前的有两种选择: 32 | 33 | 一种是跟舍友借用一下新款的笔记本,这台笔记本在传统VGA接口的基础上,增加了一个HDMI显示接口,能够满足两种显示需求(implements DisplayRequire): 34 | 35 | AdvancedComputer.java 36 | 37 | public class AdvancedComputer extends Computer implements DisplayRequire { 38 | public String transmitWithHDMI() { 39 | return this.ppt; 40 | } 41 | } 42 | 43 | 另一种选择就是去商店买一个转接头,可以将VGA信号转换为HDMI和VGA两路信号,只需要接在现在的笔记本上就行: 44 | 45 | DisplayAdaptor.java 46 | 47 | public class DisplayAdaptor implements DisplayRequire { 48 | private Computer computer; 49 | 50 | public DisplayAdaptor(Computer computer) { 51 | this.computer = computer; 52 | } 53 | 54 | public String transmitWithVGA() { 55 | return this.computer.transmitWithVGA(); 56 | } 57 | 58 | public String transmitWithHDMI() { 59 | return this.computer.transmitWithVGA(); 60 | } 61 | } 62 | 63 | 无论选择那种方式,都OK: 64 | 65 | Projector.java 66 | 67 | // 借新电脑 68 | AdvancedComputer advancedComputer = new AdvancedComputer(); 69 | System.out.println(advancedComputer.transmitWithHDMI()); 70 | System.out.println(advancedComputer.transmitWithVGA()); 71 | 72 | // 用转接头 73 | Computer computer = new Computer(); 74 | DisplayAdaptor adapter = new DisplayAdaptor(computer); 75 | System.out.println(adapter.transmitWithHDMI()); 76 | System.out.println(adapter.transmitWithVGA()); 77 | 78 | 都是可以正常输出的: 79 | 80 | My PPT report 81 | My PPT report 82 | My PPT report 83 | My PPT report 84 | 85 | # 总结 86 | 87 | 以上例子就是适配器模式,这两种方式分别代表“类适配模式”和“对象适配模式”。分别来看: 88 | 89 | 1.对于“借新电脑”这种方式,就是类适配器模式。当`Computer`无法满足需求的时候,就用一个继承子类(新款电脑)来补充其不足的功能。类关系图如下: 90 | 91 | ![类适配器模式](images/class-adaptor.png) 92 | 93 | `AdvancedComputer`继承了`Computer`,从而满足了VGA和HDMI的显示需求。 94 | 95 | 2.对于“用转接头”这种方式,就是对象适配器模式。使用一个将`Computer`对象最为适配器的一个属性,并补充`Computer`本身不足的功能。类关系图如下: 96 | 97 | ![对象适配器模式](images/object-adaptor.png) 98 | 99 | 总之,通过继承或依赖的方式,补充现有类功能的不足,使得扩展后的功能能够满足接口需要。因此可以看出,适配器通常不是在最初设计时使用的,而是解决正在服役的项目的问题,在不破坏现有类的基础上,对其进行进一步包装,(就像本文开头所说)因此也叫做包装模式,大家在读源码的时候,如果碰到`XxxxWrapper`不妨先看看,是不是适配器模式。 -------------------------------------------------------------------------------- /adaptor/images/class-adaptor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/adaptor/images/class-adaptor.png -------------------------------------------------------------------------------- /adaptor/images/object-adaptor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/adaptor/images/object-adaptor.png -------------------------------------------------------------------------------- /adaptor/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | adaptor 7 | 8 | 9 | com.getset.designpatterns 10 | get-designpatterns 11 | 1.0-SNAPSHOT 12 | ../ 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /adaptor/src/main/java/com/getset/designpatterns/adaptor/AdvancedComputer.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.adaptor; 2 | 3 | public class AdvancedComputer extends Computer implements DisplayRequire { 4 | public String transmitWithHDMI() { 5 | return this.ppt; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /adaptor/src/main/java/com/getset/designpatterns/adaptor/Computer.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.adaptor; 2 | 3 | public class Computer { 4 | 5 | protected String ppt = "My PPT report"; 6 | 7 | public String transmitWithVGA() { 8 | return ppt; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /adaptor/src/main/java/com/getset/designpatterns/adaptor/DisplayAdaptor.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.adaptor; 2 | 3 | public class DisplayAdaptor implements DisplayRequire { 4 | private Computer computer; 5 | 6 | public DisplayAdaptor(Computer computer) { 7 | this.computer = computer; 8 | } 9 | 10 | public String transmitWithVGA() { 11 | return this.computer.transmitWithVGA(); 12 | } 13 | 14 | public String transmitWithHDMI() { 15 | return this.computer.transmitWithVGA(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /adaptor/src/main/java/com/getset/designpatterns/adaptor/DisplayRequire.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.adaptor; 2 | 3 | public interface DisplayRequire { 4 | String transmitWithVGA(); 5 | String transmitWithHDMI(); 6 | } 7 | -------------------------------------------------------------------------------- /adaptor/src/main/java/com/getset/designpatterns/adaptor/Projector.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.adaptor; 2 | 3 | public class Projector { 4 | public static void main(String[] args) { 5 | // 借新电脑 6 | AdvancedComputer advancedComputer = new AdvancedComputer(); 7 | System.out.println(advancedComputer.transmitWithHDMI()); 8 | System.out.println(advancedComputer.transmitWithVGA()); 9 | 10 | // 用转接头 11 | Computer computer = new Computer(); 12 | DisplayAdaptor adapter = new DisplayAdaptor(computer); 13 | System.out.println(adapter.transmitWithHDMI()); 14 | System.out.println(adapter.transmitWithVGA()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /bridge/README.md: -------------------------------------------------------------------------------- 1 | 桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。 2 | 3 | 上面这段是抄来的,读下来还是有点懵逼,不过并不是没用,我们还是老规矩,先从一个例子入手,然后回头再来回味这段话,就会有更深入的理解。 4 | 5 | # 例子 6 | 7 | 相信大家都玩过或正在玩网游,不知道有没有玩过《魔兽世界》,啊~那是我逝去的青春啊~想当年,我是个快乐的,内心兼备光明与黑暗的牧师。当我使用“神圣”天赋的时候,我是一名救死扶伤的奶妈,给队友加血;当我使用“暗影”天赋的时候,我是一名嫉恶如仇的战士,给敌人伤害。 8 | 9 | 牧师可以使用多种武器,比如锤子和魔杖。一把锤子或魔杖在切换不同天赋的时候都能使用,在“神圣”天赋的时候,武器具备增加治疗的属性,使用的时候发出金黄色的光芒,可以给队友和自己加血;在“暗影”天赋的时候,武器具备增加伤害的属性,使用的时候发出黑紫色的光芒,可以对敌人造成伤害。 10 | 11 | 无论使用什么武器,都需要施法,施法前要吟唱,然后挥舞一下武器放出法术,最后收回。吟唱的时候微微发光,放出法术的时候光芒从武器到目标任务,收回的时候光芒消失。那么对于法术来说如下设计: 12 | 13 | Enchantment.java 14 | 15 | public interface Enchantment { 16 | void onActivate(); 17 | void apply(); 18 | void onDeactivate(); 19 | } 20 | 21 | HolyEnchantment.java 22 | 23 | public class HolyEnchantment implements Enchantment { 24 | public void onActivate() { 25 | System.out.println("武器逐渐泛起金黄色的圣光..."); 26 | } 27 | 28 | public void apply() { 29 | System.out.println("一道金黄色的圣光从武器发出,传到队友身上,队友加血1000"); 30 | } 31 | 32 | public void onDeactivate() { 33 | System.out.println("武器的光芒迅速消失..."); 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "神圣魔法"; 39 | } 40 | } 41 | 42 | ShadowEnchantment.java 43 | 44 | public class ShadowEnchantment implements Enchantment { 45 | public void onActivate() { 46 | System.out.println("武器逐渐泛起黑紫色的暗影..."); 47 | } 48 | 49 | public void apply() { 50 | System.out.println("一道黑紫色的暗影从武器发出,传到敌人身上,敌人掉血2000"); 51 | } 52 | 53 | public void onDeactivate() { 54 | System.out.println("武器的暗影迅速消失..."); 55 | } 56 | 57 | @Override 58 | public String toString() { 59 | return "暗影魔法"; 60 | } 61 | } 62 | 63 | 对于武器来说,进行如下设计: 64 | 65 | Weapon.java 66 | 67 | public abstract class Weapon { 68 | protected Enchantment enchantment; 69 | abstract void chant(); 70 | abstract void wield(); 71 | abstract void retrieve(); 72 | public void setEnchantment(Enchantment enchantment) { 73 | this.enchantment = enchantment; 74 | } 75 | public Enchantment getEnchantment() { 76 | return this.enchantment; 77 | } 78 | } 79 | 80 | Hammer.java 81 | 82 | public class Hammer extends Weapon { 83 | 84 | public Hammer(Enchantment enchantment) { 85 | this.enchantment = enchantment; 86 | } 87 | 88 | public void chant() { 89 | System.out.print("牧师拿出锤子,口中吟唱神圣治疗祷言..."); 90 | enchantment.onActivate(); 91 | } 92 | 93 | public void wield() { 94 | System.out.print("牧师将锤子举过头顶挥舞了一下..."); 95 | enchantment.apply(); 96 | } 97 | 98 | public void retrieve() { 99 | System.out.print("牧师收回锤子..."); 100 | enchantment.onDeactivate(); 101 | } 102 | } 103 | 104 | Wand.java 105 | 106 | public class Wand extends Weapon { 107 | public Wand(Enchantment enchantment) { 108 | this.enchantment = enchantment; 109 | } 110 | 111 | public void chant() { 112 | System.out.print("牧师拿出魔杖,口中吟唱神圣治疗祷言..."); 113 | enchantment.onActivate(); 114 | } 115 | 116 | public void wield() { 117 | System.out.print("牧师将魔杖举过头顶挥舞了一下..."); 118 | enchantment.apply(); 119 | } 120 | 121 | public void retrieve() { 122 | System.out.print("牧师收回魔杖..."); 123 | enchantment.onDeactivate(); 124 | } 125 | } 126 | 127 | 测试一下效果: 128 | 129 | Client.java 130 | 131 | public class Client { 132 | public static void main(String[] args) { 133 | System.out.println("进入副本,使用神圣天赋 >>>"); 134 | Enchantment enchantment = new HolyEnchantment(); 135 | Wand wand = new Wand(enchantment); 136 | wand.chant(); 137 | wand.wield(); 138 | wand.retrieve(); 139 | 140 | System.out.println("野外打怪,使用暗影天赋 >>>"); 141 | enchantment = new ShadowEnchantment(); 142 | Hammer hammer = new Hammer(enchantment); 143 | hammer.chant(); 144 | hammer.wield(); 145 | hammer.retrieve(); 146 | } 147 | } 148 | 149 | 输出如下: 150 | 151 | 进入副本,使用神圣天赋 >>> 152 | 牧师拿出魔杖,口中吟唱神圣治疗祷言...武器逐渐泛起金黄色的圣光... 153 | 牧师将魔杖举过头顶挥舞了一下...一道金黄色的圣光从武器发出,传到队友身上,队友加血1000 154 | 牧师收回魔杖...武器的光芒迅速消失... 155 | 野外打怪,使用暗影天赋 >>> 156 | 牧师拿出锤子,口中吟唱神圣治疗祷言...武器逐渐泛起黑紫色的暗影... 157 | 牧师将锤子举过头顶挥舞了一下...一道黑紫色的暗影从武器发出,传到敌人身上,敌人掉血2000 158 | 牧师收回锤子...武器的暗影迅速消失... 159 | 160 | # 总结 161 | 162 | 我们再来看一下文章开头的话“把抽象化与实现化解耦”。在理解这句话前,我们先看看本例为什么这么设计。 163 | 164 | 本例在设计的时候,将魔法作为武器的成员进行了组合。如果不使用组合这种方式呢?先不考虑Java是否能多继承,对于一把武器一次特定的使用,需要继承某种`Weapon`和某种`Enchantment`,这样就有了“神圣锤子”、“暗影锤子”、“神圣魔杖”和“暗影魔杖”。 165 | 166 | 其实我们这里遇到的是一个`M x N`的问题。对于一把武器来说,可能是锤子可能是魔杖,而且比如有魔法才能使用,可能是神圣魔法,可能是暗影魔法,可见这是个`2 x 2`的问题。 167 | 168 | 那么如果游戏可玩性增加,牧师又多了个天赋(实际上牧师还有个“戒律”天赋),这个时候需要增加“戒律锤子”和“戒律魔杖”,现在是`2 x 3`了。后来牧师又可以拿剑和魔法书了,估计开发人员要一头撞死了。。。 169 | 170 | 实现系统可能有多个角度分类,每一种角度都可能变化,这里武器类型和天赋类型就是两个角度,它们分别会有变化。这两个角度都有不同的抽象(比如武器和天赋)和实现(比如锤子、魔杖;神圣、暗影)。把这种多角度分类给分离出来让他们独立变化,减少他们之间耦合,就能够有效提高扩展性,如何做到呢? 171 | 172 | 就如同本例的方式,**使用抽象化和实现化之间使用关联关系(组合或者聚合关系)而不是继承关系,从而使两者可以相对独立地变化**,这就是桥接模式的用意。所以解耦就是将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联(可以理解成编译时确定的关联)改换成弱关联(可以理解成运行时才确定的关联),将两个角色之间的继承关系改为关联关系。 173 | 174 | 所以,桥接模式的使用场景如下: 175 | 1. 如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。 176 | 2. 对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。 177 | 3. 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展,就是`M x N`问题,让M和N自己玩儿。 -------------------------------------------------------------------------------- /bridge/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | bridge 7 | 8 | 9 | com.getset.designpatterns 10 | get-designpatterns 11 | 1.0-SNAPSHOT 12 | ../ 13 | 14 | 15 | -------------------------------------------------------------------------------- /bridge/src/main/java/com/getset/designpatterns/bridge/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.bridge; 2 | 3 | public class Client { 4 | public static void main(String[] args) { 5 | System.out.println("进入副本,使用神圣天赋 >>>"); 6 | Enchantment enchantment = new HolyEnchantment(); 7 | Wand wand = new Wand(enchantment); 8 | wand.chant(); 9 | wand.wield(); 10 | wand.retrieve(); 11 | 12 | System.out.println("野外打怪,使用暗影天赋 >>>"); 13 | enchantment = new ShadowEnchantment(); 14 | Hammer hammer = new Hammer(enchantment); 15 | hammer.chant(); 16 | hammer.wield(); 17 | hammer.retrieve(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /bridge/src/main/java/com/getset/designpatterns/bridge/Enchantment.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.bridge; 2 | 3 | public interface Enchantment { 4 | void onActivate(); 5 | void apply(); 6 | void onDeactivate(); 7 | } 8 | -------------------------------------------------------------------------------- /bridge/src/main/java/com/getset/designpatterns/bridge/Hammer.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.bridge; 2 | 3 | public class Hammer extends Weapon { 4 | 5 | public Hammer(Enchantment enchantment) { 6 | this.enchantment = enchantment; 7 | } 8 | 9 | public void chant() { 10 | System.out.print("牧师拿出锤子,口中吟唱神圣治疗祷言..."); 11 | enchantment.onActivate(); 12 | } 13 | 14 | public void wield() { 15 | System.out.print("牧师将锤子举过头顶挥舞了一下..."); 16 | enchantment.apply(); 17 | } 18 | 19 | public void retrieve() { 20 | System.out.print("牧师收回锤子..."); 21 | enchantment.onDeactivate(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /bridge/src/main/java/com/getset/designpatterns/bridge/HolyEnchantment.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.bridge; 2 | 3 | public class HolyEnchantment implements Enchantment { 4 | public void onActivate() { 5 | System.out.println("武器逐渐泛起金黄色的圣光..."); 6 | } 7 | 8 | public void apply() { 9 | System.out.println("一道金黄色的圣光从武器发出,传到队友身上,队友加血1000"); 10 | } 11 | 12 | public void onDeactivate() { 13 | System.out.println("武器的光芒迅速消失..."); 14 | } 15 | 16 | @Override 17 | public String toString() { 18 | return "神圣魔法"; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /bridge/src/main/java/com/getset/designpatterns/bridge/ShadowEnchantment.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.bridge; 2 | 3 | public class ShadowEnchantment implements Enchantment { 4 | public void onActivate() { 5 | System.out.println("武器逐渐泛起黑紫色的暗影..."); 6 | } 7 | 8 | public void apply() { 9 | System.out.println("一道黑紫色的暗影从武器发出,传到敌人身上,敌人掉血2000"); 10 | } 11 | 12 | public void onDeactivate() { 13 | System.out.println("武器的暗影迅速消失..."); 14 | } 15 | 16 | @Override 17 | public String toString() { 18 | return "暗影魔法"; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /bridge/src/main/java/com/getset/designpatterns/bridge/Wand.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.bridge; 2 | 3 | public class Wand extends Weapon { 4 | 5 | public Wand(Enchantment enchantment) { 6 | this.enchantment = enchantment; 7 | } 8 | 9 | public void chant() { 10 | System.out.print("牧师拿出魔杖,口中吟唱神圣治疗祷言..."); 11 | enchantment.onActivate(); 12 | } 13 | 14 | public void wield() { 15 | System.out.print("牧师将魔杖举过头顶挥舞了一下..."); 16 | enchantment.apply(); 17 | } 18 | 19 | public void retrieve() { 20 | System.out.print("牧师收回魔杖..."); 21 | enchantment.onDeactivate(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /bridge/src/main/java/com/getset/designpatterns/bridge/Weapon.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.bridge; 2 | 3 | public abstract class Weapon { 4 | protected Enchantment enchantment; 5 | abstract void chant(); 6 | abstract void wield(); 7 | abstract void retrieve(); 8 | public void setEnchantment(Enchantment enchantment) { 9 | this.enchantment = enchantment; 10 | } 11 | public Enchantment getEnchantment() { 12 | return this.enchantment; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /builder/README.md: -------------------------------------------------------------------------------- 1 | 建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象,这种类型的设计模式属于创建型模式。建造模式可以将一个复杂对象的内部组成部分,与该对象本身的创建分离开来,从而使得复杂对象的组装更灵活。 2 | 3 | 文绉绉的话不宜多说,其实这种模式还是挺常见的: 4 | * 比如我们在订手机套餐的时候,无论是自选还是电信公司配置好的,通常一个套餐包括:多少分钟市话、多少条短信、多少G的省内和省外流量等,这几样通常得有,但是具体选多少可以自由搭配。 5 | * 比如我们玩网游创建任务的时候,要选择职业、肤色、发型;还有游戏中给自己角色搭配装备的时候,要配置帽子、肩部、披风、上衣、裤子、鞋子等等,还有一系列配饰,十几个框框所有人都是一样的,不同的是放什么样的装备进去。 6 | 7 | # 例子 8 | 就以刚才提到的网游人物的配置为例: 9 | 我们在创建游戏任务的时候,必须填写任务名称、选择职业或种族,然后可以自行配置角色的发型、发色、武器、铠甲等等,当然后边这些有可能是根据职业或种族随机分配的。 10 | 11 | 首先是角色的发型、发色、武器、铠甲,以及职业: 12 | 13 | HairType.java 14 | 15 | public enum HairType { 16 | BALD("bald"), SHORT("short"), CURLY("curly"), LONG_STRAIGHT("long straight"), LONG_CURLY("long curly"); 17 | private String title; 18 | HairType(String title) { 19 | this.title = title; 20 | } 21 | @Override 22 | public String toString() { 23 | return title; 24 | } 25 | } 26 | 27 | HairColor.java 28 | 29 | public enum HairColor { 30 | WHITE, BLOND, RED, BROWN, BLACK; 31 | @Override 32 | public String toString() { 33 | return name().toLowerCase(); 34 | } 35 | } 36 | 37 | Weapon.java 38 | 39 | public enum Weapon { 40 | DAGGER, SWORD, AXE, WARHAMMER, BOW; 41 | @Override 42 | public String toString() { 43 | return name().toLowerCase(); 44 | } 45 | } 46 | 47 | Armor.java 48 | 49 | public enum Armor { 50 | CLOTHES("clothes"), LEATHER("leather"), CHAIN_MAIL("chain mail"), PLATE_MAIL("plate mail"); 51 | private String title; 52 | Armor(String title) { 53 | this.title = title; 54 | } 55 | @Override 56 | public String toString() { 57 | return title; 58 | } 59 | } 60 | 61 | Profession.java 62 | 63 | public enum Profession { 64 | WARRIOR, THIEF, MAGE, PRIEST; 65 | @Override 66 | public String toString() { 67 | return name().toLowerCase(); 68 | } 69 | } 70 | 71 | 在设计具体实现的时候,我们参考《Effective Java》第二条”遇到多个构造器参数时要考虑用构建器“中的内容组织类之间的关系。将Builder类内置于角色内部。 72 | 73 | Hero.java 74 | 75 | public final class Hero { 76 | 77 | private final Profession profession; 78 | private final String name; 79 | private final HairType hairType; 80 | private final HairColor hairColor; 81 | private final Armor armor; 82 | private final Weapon weapon; 83 | 84 | private Hero(Builder builder) { 85 | this.profession = builder.profession; 86 | this.name = builder.name; 87 | this.hairColor = builder.hairColor; 88 | this.hairType = builder.hairType; 89 | this.weapon = builder.weapon; 90 | this.armor = builder.armor; 91 | } 92 | 93 | // getters 94 | 95 | @Override 96 | public String toString() { 97 | ... ... 98 | } 99 | 100 | /** 101 | * The builder class. 102 | */ 103 | public static class Builder { 104 | 105 | private final Profession profession; 106 | private final String name; 107 | private HairType hairType; 108 | private HairColor hairColor; 109 | private Armor armor; 110 | private Weapon weapon; 111 | 112 | /** 113 | * Constructor 114 | */ 115 | public Builder(Profession profession, String name) { 116 | if (profession == null || name == null) { 117 | throw new IllegalArgumentException("profession and name can not be null"); 118 | } 119 | this.profession = profession; 120 | this.name = name; 121 | } 122 | 123 | public Builder withHairType(HairType hairType) { 124 | this.hairType = hairType; 125 | return this; 126 | } 127 | 128 | public Builder withHairColor(HairColor hairColor) { 129 | this.hairColor = hairColor; 130 | return this; 131 | } 132 | 133 | public Builder withArmor(Armor armor) { 134 | this.armor = armor; 135 | return this; 136 | } 137 | 138 | public Builder withWeapon(Weapon weapon) { 139 | this.weapon = weapon; 140 | return this; 141 | } 142 | 143 | public Hero build() { 144 | return new Hero(this); 145 | } 146 | } 147 | } 148 | 149 | 建造者模式的主要内容其实就在这个`Hero.java`类中了。 150 | 151 | 我们可以看到`Hero`类中有个子类`Hero.Builder`,具体`Hero`的创建由Builder来完成。可以看到`Hero`只有一个以`Build`为参数的构造函数,所以将只接受这种形式的创建。 152 | 153 | > 是不是想到了[单例模式](../singleton)中的那个使用静态内部类来保管单一实例的例子了呢。其实是有相通之处的,我们说过,静态内部类可以看作恰好定义在另一个类内部的普通类。与单例不同的是,这个静态内部类`Builder`由于需要在外部使用,是`public`的。之所以放在内部是因为内部类同外部类之间可以访问所有的成员,更加方便。 154 | 155 | 那么在使用的使用的时候,就可以通过`Builder`定义的一系列“组装方法”来创建实例了: 156 | 157 | Hero warrior = new Hero.Builder(Profession.WARRIOR, "Amberjill").withHairColor(HairColor.BLOND) 158 | .withHairType(HairType.LONG_CURLY).withArmor(Armor.CHAIN_MAIL).withWeapon(Weapon.SWORD) 159 | .build(); 160 | 161 | 这样一个“战士”就诞生了,突然有点想玩网游了呢~ 162 | 163 | # 总结 164 | 建造者模式通常应用于:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。 165 | 166 | 最后给一个小作业可以考虑一下用建造者模式实现: 167 | 假设张三要盖房,交钥匙拎包入住那种。他找到服务商,说希望盖五间大瓦房、刮大白简装、配置便宜家居,谈好合同后,服务商找到施工队去盖房、装修和配便宜家具。另外又有个叫李四的人也找到这个服务商,说希望盖一个小洋楼、精装修、配实木家具,服务商又找到另一个擅长小洋楼的施工队去实施。 168 | 169 | 170 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /builder/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | builder 7 | 8 | 9 | com.getset.designpatterns 10 | get-designpatterns 11 | 1.0-SNAPSHOT 12 | ../ 13 | 14 | 15 | -------------------------------------------------------------------------------- /builder/src/main/java/com/getset/designpatterns/builder/Armor.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.builder; 2 | 3 | /** 4 | * 铠甲 5 | */ 6 | public enum Armor { 7 | 8 | CLOTHES("clothes"), LEATHER("leather"), CHAIN_MAIL("chain mail"), PLATE_MAIL("plate mail"); 9 | 10 | private String title; 11 | 12 | Armor(String title) { 13 | this.title = title; 14 | } 15 | 16 | @Override 17 | public String toString() { 18 | return title; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /builder/src/main/java/com/getset/designpatterns/builder/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.builder; 2 | 3 | public class Client { 4 | public static void main(String[] args) { 5 | Hero mage = 6 | new Hero.Builder(Profession.MAGE, "Riobard").withHairColor(HairColor.BLACK) 7 | .withWeapon(Weapon.DAGGER).build(); 8 | System.out.println(mage); 9 | 10 | Hero warrior = 11 | new Hero.Builder(Profession.WARRIOR, "Amberjill").withHairColor(HairColor.BLOND) 12 | .withHairType(HairType.LONG_CURLY).withArmor(Armor.CHAIN_MAIL).withWeapon(Weapon.SWORD) 13 | .build(); 14 | System.out.println(warrior); 15 | 16 | Hero thief = 17 | new Hero.Builder(Profession.THIEF, "Desmond").withHairType(HairType.BALD) 18 | .withWeapon(Weapon.BOW).build(); 19 | System.out.println(thief); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /builder/src/main/java/com/getset/designpatterns/builder/HairColor.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.builder; 2 | 3 | /** 4 | * 发色 5 | */ 6 | public enum HairColor { 7 | 8 | WHITE, BLOND, RED, BROWN, BLACK; 9 | 10 | @Override 11 | public String toString() { 12 | return name().toLowerCase(); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /builder/src/main/java/com/getset/designpatterns/builder/HairType.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.builder; 2 | 3 | /** 4 | * 发型 5 | */ 6 | public enum HairType { 7 | 8 | BALD("bald"), SHORT("short"), CURLY("curly"), LONG_STRAIGHT("long straight"), LONG_CURLY( 9 | "long curly"); 10 | 11 | private String title; 12 | 13 | HairType(String title) { 14 | this.title = title; 15 | } 16 | 17 | @Override 18 | public String toString() { 19 | return title; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /builder/src/main/java/com/getset/designpatterns/builder/Hero.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.builder; 2 | 3 | /** 4 | * 英雄角色 5 | */ 6 | public final class Hero { 7 | 8 | private final Profession profession; 9 | private final String name; 10 | private final HairType hairType; 11 | private final HairColor hairColor; 12 | private final Armor armor; 13 | private final Weapon weapon; 14 | 15 | private Hero(Builder builder) { 16 | this.profession = builder.profession; 17 | this.name = builder.name; 18 | this.hairColor = builder.hairColor; 19 | this.hairType = builder.hairType; 20 | this.weapon = builder.weapon; 21 | this.armor = builder.armor; 22 | } 23 | 24 | public Profession getProfession() { 25 | return profession; 26 | } 27 | 28 | public String getName() { 29 | return name; 30 | } 31 | 32 | public HairType getHairType() { 33 | return hairType; 34 | } 35 | 36 | public HairColor getHairColor() { 37 | return hairColor; 38 | } 39 | 40 | public Armor getArmor() { 41 | return armor; 42 | } 43 | 44 | public Weapon getWeapon() { 45 | return weapon; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | 51 | StringBuilder sb = new StringBuilder(); 52 | sb.append("This is a ") 53 | .append(profession) 54 | .append(" named ") 55 | .append(name); 56 | if (hairColor != null || hairType != null) { 57 | sb.append(" with "); 58 | if (hairColor != null) { 59 | sb.append(hairColor).append(' '); 60 | } 61 | if (hairType != null) { 62 | sb.append(hairType).append(' '); 63 | } 64 | sb.append(hairType != HairType.BALD ? "hair" : "head"); 65 | } 66 | if (armor != null) { 67 | sb.append(" wearing ").append(armor); 68 | } 69 | if (weapon != null) { 70 | sb.append(" and wielding a ").append(weapon); 71 | } 72 | sb.append('.'); 73 | return sb.toString(); 74 | } 75 | 76 | /** 77 | * The builder class. 78 | */ 79 | public static class Builder { 80 | 81 | private final Profession profession; 82 | private final String name; 83 | private HairType hairType; 84 | private HairColor hairColor; 85 | private Armor armor; 86 | private Weapon weapon; 87 | 88 | /** 89 | * Constructor 90 | */ 91 | public Builder(Profession profession, String name) { 92 | if (profession == null || name == null) { 93 | throw new IllegalArgumentException("profession and name can not be null"); 94 | } 95 | this.profession = profession; 96 | this.name = name; 97 | } 98 | 99 | public Builder withHairType(HairType hairType) { 100 | this.hairType = hairType; 101 | return this; 102 | } 103 | 104 | public Builder withHairColor(HairColor hairColor) { 105 | this.hairColor = hairColor; 106 | return this; 107 | } 108 | 109 | public Builder withArmor(Armor armor) { 110 | this.armor = armor; 111 | return this; 112 | } 113 | 114 | public Builder withWeapon(Weapon weapon) { 115 | this.weapon = weapon; 116 | return this; 117 | } 118 | 119 | public Hero build() { 120 | return new Hero(this); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /builder/src/main/java/com/getset/designpatterns/builder/Profession.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.builder; 2 | 3 | /** 4 | * 职业 5 | */ 6 | public enum Profession { 7 | WARRIOR, THIEF, MAGE, PRIEST; 8 | 9 | @Override 10 | public String toString() { 11 | return name().toLowerCase(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /builder/src/main/java/com/getset/designpatterns/builder/Weapon.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License 3 | * Copyright (c) 2014-2016 Ilkka Seppälä 4 | *

5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | *

12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | *

15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package com.getset.designpatterns.builder; 24 | 25 | /** 26 | * 武器 27 | */ 28 | public enum Weapon { 29 | DAGGER, SWORD, AXE, WARHAMMER, BOW; 30 | 31 | @Override 32 | public String toString() { 33 | return name().toLowerCase(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /chainofresponsibility/README.md: -------------------------------------------------------------------------------- 1 | 责任链模式(Chain of Responsibility Pattern)中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。这种类型的设计模式属于行为型模式。 2 | 3 | # 例子 4 | 5 | 话不多说,先看下边两个图: 6 | 7 | ![责任链示例](images/chainofresponsibility-example.png) 8 | 9 | 相信你一眼就明白了,没错,这就是责任链模式的现实场景。 10 | 1. 第一个是公司内的责任链图,作为基层员工,许多事情要请示。有些事情组长就可以做主,有些事情要部门经理才能批准,但是作为基层员工通常不会直接越级找部门经理,而是通过上级层层上报。 11 | 2. 第二个是空气净化器的净化流程,先过滤大颗粒粉尘,然后是小颗粒PM2.5,然后是吸附甲醛和异味物质... 12 | 13 | 这两个例子有些明显的区别: 14 | 1. 第一个例子,每个环节并不一定要做具体处理,有的直接转给下一个环节去处理;而第二个例子,每个环节都会做相应处理。 15 | 2. 第一个例子,到具体执行环节处理完后,就不会再向下一个节点流转;而第二个例子,空气要做过每层过滤处理最终才能出来。 16 | 17 | 这两种其实都是责任链模式。也就是责任链模式不care上述这些(白眼,那你说这么多,浪费哥的宝贵时间)。**责任链模型关注于单个环节,而不是整体流程**。 18 | 19 | ![类图](images/chainofresponsibility.png) 20 | 21 | 这是我直接从《Java与模式》中截的图,看到这里是不是有种“链表”的既视感,要想做成“链”,每个节点就要有下一个节点的引用,然后每个节点有相应的处理方法,好啦,齐活啦~那么流程链呢,由外部业务逻辑去实现就OK了。 22 | 23 | 下面这个具体的代码例子,我也是拿来主义,哈哈,不是今天偷懒,而是感觉这个例子挺好,来自[菜鸟教程的关于日志的例子](http://www.runoob.com/design-pattern/chain-of-responsibility-pattern.html)。 24 | 25 | 我们做应用程序离不开日志,日志有多个等级,通常从高到低有ERROR、WARN、INFO、DEBUG等。 26 | 1. 日志记录的时候设置的等级越低,那么就会记录越多的日志。比如要求日志记录到DEBUG级,那么ERROR、WARN、INFO、DEBUG这些日志都会打印出来;如果要求日志记录到WARN级,那么只打印出ERROR和WARN级别的日志。 27 | 2. 日志打印在哪呢,有标准输出、标准错误、文件等不同的输出流。这些输出流可以设置不同的日志级别,就像上一条那样。 28 | 29 | 背景介绍完,可以看代码了~ 30 | 31 | 抽象类 AbstractLogger带有详细的日志记录级别。然后创建三种类型的记录器,都扩展了AbstractLogger。每个记录器消息的级别是否属于自己的级别,如果是则相应地打印出来,否则将不打印并把消息传给下一个记录器。 32 | 33 | ![类图](images/chain_pattern_uml_diagram.jpg) 34 | 35 | 创建抽象的记录器类`AbstractLogger`,`ConsoleLogger`、`ErrorLogger`和`FileLogger`是扩展了的具体记录器类。它们就是不同的责任链节点,根据自己的日志记录级别打印出日志。 36 | 37 | 拿来主义的代码就不贴了哈,可以看一下[这个链接](http://www.runoob.com/design-pattern/chain-of-responsibility-pattern.html)。 38 | 39 | # 总结 40 | 41 | 关于责任链模式想必你有了一个感性的认识,我不喜欢在文章中罗列设计模式的各种角色、使用场景、优点、缺点,感性的理解最重要,说到底对设计模式的理解是对面向对象设计原则的理解。一方面,设计模式不仅仅是这23种,另一方面,具体某种设计模式在使用时也会有不同变化,不同的设计模式也可以结合使用。 42 | 43 | 因此设计模式要抓住特征,就像美术功底不高的人素描画人物我们觉得不像,但是画简笔画的人抓住特征画出来的动漫大头人形象,我们一看就知道这是谁。一样的道理。 44 | 45 | 责任链模式的特征就在于“链”。如何实现这个链呢,就是通过节点“接力”,每个节点指定好下个节点,这样串起来就好。像流水线一样,各个节点处理自己分内的工作。 46 | 47 | 好处也是显而易见的,就像流水线,如果增加了一道工具,那么接在合适的流程位置即可,对于责任链模式来说没有任何影响,因为流水线的构造是由业务逻辑定义的。 48 | 49 | 责任链模式在许多我们熟知的Java框架或技术中都有应用。 50 | 1. 写过Servlet的同学肯定对`Filter`都有印象,我们可以定义多个Filter,这些Filter串起来就是一个“FilterChain”,来自浏览器的请求过来之后,首先经过层层Filter处理,然后到达映射的Servlet,感觉是不是有点像“空气过滤器”啊? 51 | 2. Tomcat中也应用到了责任链模式,这个可能有些同学不太清楚。Tomcat是一个Servlet容器的实现,我们编写的Servlet就是用这个容器托管起来了,请求进来之后,由Tomcat接管,然后转交给具体Servlet处理,然后再有Tomcat将处理反馈发送回去。 52 | * 其实这个容器是由一层层的类似于“俄罗斯套娃”的容器嵌套而成的,配置过Tomcat的server.xml的同学可能有印象,标签层级是``我们的应用程序作为``是配置在最里边的。 53 | * 那么请求进来之后,如果经过层层套娃到达最里边的也就是我们的应用呢?就是用的责任链模式,从Engine到Host再到Context一直到Wrapper(是的,还有一层套娃。。。)都通过一个链传递请求。 54 | -------------------------------------------------------------------------------- /chainofresponsibility/images/chain_pattern_uml_diagram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/chainofresponsibility/images/chain_pattern_uml_diagram.jpg -------------------------------------------------------------------------------- /chainofresponsibility/images/chainofresponsibility-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/chainofresponsibility/images/chainofresponsibility-example.png -------------------------------------------------------------------------------- /chainofresponsibility/images/chainofresponsibility.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/chainofresponsibility/images/chainofresponsibility.png -------------------------------------------------------------------------------- /chainofresponsibility/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | chainofresponsibility 8 | 9 | 10 | com.getset.designpatterns 11 | get-designpatterns 12 | 1.0-SNAPSHOT 13 | ../ 14 | 15 | 16 | -------------------------------------------------------------------------------- /chainofresponsibility/src/main/java/com/getset/designpatterns/chainofresponsibility/AbstractLogger.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.chainofresponsibility; 2 | 3 | public abstract class AbstractLogger { 4 | public static int INFO = 1; 5 | public static int DEBUG = 2; 6 | public static int ERROR = 3; 7 | 8 | protected int level; 9 | 10 | //责任链中的下一个元素 11 | protected AbstractLogger nextLogger; 12 | 13 | public void setNextLogger(AbstractLogger nextLogger){ 14 | this.nextLogger = nextLogger; 15 | } 16 | 17 | public void logMessage(int level, String message){ 18 | if(this.level <= level){ 19 | write(message); 20 | } 21 | if(nextLogger !=null){ 22 | nextLogger.logMessage(level, message); 23 | } 24 | } 25 | 26 | abstract protected void write(String message); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /chainofresponsibility/src/main/java/com/getset/designpatterns/chainofresponsibility/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.chainofresponsibility; 2 | 3 | public class Client { 4 | 5 | private static AbstractLogger getChainOfLoggers() { 6 | 7 | AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR); 8 | AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG); 9 | AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO); 10 | 11 | errorLogger.setNextLogger(fileLogger); 12 | fileLogger.setNextLogger(consoleLogger); 13 | 14 | return errorLogger; 15 | } 16 | 17 | public static void main(String[] args) { 18 | AbstractLogger loggerChain = getChainOfLoggers(); 19 | 20 | loggerChain.logMessage(AbstractLogger.INFO, 21 | "This is an information."); 22 | 23 | loggerChain.logMessage(AbstractLogger.DEBUG, 24 | "This is an debug level information."); 25 | 26 | loggerChain.logMessage(AbstractLogger.ERROR, 27 | "This is an error information."); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /chainofresponsibility/src/main/java/com/getset/designpatterns/chainofresponsibility/ConsoleLogger.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.chainofresponsibility; 2 | 3 | public class ConsoleLogger extends AbstractLogger { 4 | 5 | public ConsoleLogger(int level){ 6 | this.level = level; 7 | } 8 | 9 | @Override 10 | protected void write(String message) { 11 | System.out.println("Standard Console::Logger: " + message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /chainofresponsibility/src/main/java/com/getset/designpatterns/chainofresponsibility/ErrorLogger.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.chainofresponsibility; 2 | 3 | public class ErrorLogger extends AbstractLogger { 4 | 5 | public ErrorLogger(int level){ 6 | this.level = level; 7 | } 8 | 9 | @Override 10 | protected void write(String message) { 11 | System.out.println("Error Console::Logger: " + message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /chainofresponsibility/src/main/java/com/getset/designpatterns/chainofresponsibility/FileLogger.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.chainofresponsibility; 2 | 3 | public class FileLogger extends AbstractLogger { 4 | 5 | public FileLogger(int level){ 6 | this.level = level; 7 | } 8 | 9 | @Override 10 | protected void write(String message) { 11 | System.out.println("File::Logger: " + message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /command/images/after-decoupling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/command/images/after-decoupling.png -------------------------------------------------------------------------------- /command/images/before-decoupling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/command/images/before-decoupling.png -------------------------------------------------------------------------------- /command/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | command 8 | 9 | 10 | com.getset.designpatterns 11 | get-designpatterns 12 | 1.0-SNAPSHOT 13 | ../ 14 | 15 | 16 | -------------------------------------------------------------------------------- /command/src/main/java/com/getset/designpatterns/command/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.command; 2 | 3 | public class Client { 4 | public static void main(String[] args) { 5 | DrawingApp app = new DrawingApp(); 6 | ShapeDrawer shapeDrawer = new ShapeDrawer(); 7 | ColorFiller colorFiller = new ColorFiller(); 8 | Command drawCircle = new ShapeDrawing(shapeDrawer, "圆形"); 9 | Command fillRed = new ColorFilling(colorFiller, "红色"); 10 | Command drawRectancle = new ShapeDrawing(shapeDrawer, "矩形"); 11 | app.takeCommand(drawCircle); 12 | app.takeCommand(fillRed); 13 | app.takeCommand(drawRectancle); 14 | app.CommandsDone(); 15 | app.undoLastCommand(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /command/src/main/java/com/getset/designpatterns/command/ColorFiller.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.command; 2 | 3 | public class ColorFiller { 4 | public void fillColor(String color) { 5 | System.out.println("填充" + color); 6 | } 7 | public void unfillColor() { 8 | System.out.println("撤销填充的颜色"); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /command/src/main/java/com/getset/designpatterns/command/ColorFilling.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.command; 2 | 3 | public class ColorFilling implements Command { 4 | private ColorFiller filler; 5 | private String arg; 6 | 7 | public ColorFilling(ColorFiller filler, String arg) { 8 | this.filler = filler; 9 | this.arg = arg; 10 | } 11 | 12 | public void doCmd() { 13 | filler.fillColor(arg); 14 | } 15 | 16 | public void undoCmd() { 17 | filler.unfillColor(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /command/src/main/java/com/getset/designpatterns/command/Command.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.command; 2 | 3 | public interface Command { 4 | void doCmd(); 5 | void undoCmd(); 6 | } 7 | -------------------------------------------------------------------------------- /command/src/main/java/com/getset/designpatterns/command/DrawingApp.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.command; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class DrawingApp { 7 | private List commands = new ArrayList(); 8 | public void takeCommand(Command command) { 9 | commands.add(command); 10 | } 11 | public void CommandsDone() { 12 | for (Command command:commands) { 13 | command.doCmd(); 14 | } 15 | } 16 | public void undoLastCommand() { 17 | commands.get(commands.size() - 1).undoCmd(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /command/src/main/java/com/getset/designpatterns/command/ShapeDrawer.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.command; 2 | 3 | public class ShapeDrawer { 4 | public void drawShape(String shape) { 5 | System.out.println("画了一个" + shape); 6 | } 7 | public void undrawShape() { 8 | System.out.println("撤销刚才画的形状"); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /command/src/main/java/com/getset/designpatterns/command/ShapeDrawing.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.command; 2 | 3 | public class ShapeDrawing implements Command { 4 | private ShapeDrawer drawer; 5 | private String arg; 6 | 7 | public ShapeDrawing(ShapeDrawer drawer, String arg) { 8 | this.drawer = drawer; 9 | this.arg = arg; 10 | } 11 | 12 | public void doCmd() { 13 | drawer.drawShape(arg); 14 | } 15 | 16 | public void undoCmd() { 17 | drawer.undrawShape(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /composite/README.md: -------------------------------------------------------------------------------- 1 | 组合模式(Composite Pattern),又叫部分整体模式,依据树形结构来组合对象,是用来表示部分以及整体层次的一种递归式结构的模式。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。 2 | 3 | 其实现实世界中,这种树状结构的组合还是挺普遍的: 4 | 1. 组织结构,从CEO到基层组长,都有下属,他们都相当于“枝干”,对于基层员工就是“叶子”节点,不过说到底他们都是公司员工; 5 | 2. 目录结构,这个更好理解了,目录里包含子目录和文件,各层目录相当于树状结构的“枝干”,文件相当于“叶子”节点,说到底目录和文件都是文件系统的组件(在Linux里,都可以看作“文件”这个大的概念,甚至鼠标键盘等设备); 6 | 3. 一句文字,无论是书信还是微博,都是由字、词构成的,词呢又是由字构成的,字和词都可以看作句子的组成元素,二者又有包含关系。 7 | 8 | 看到这里是不是觉得已经不用再看具体例子的代码了呢,聪明的你估计脑海中已经构思除了一个小的demo了。 9 | 10 | # 例子 11 | 12 | 就以文件系统目录结构为例吧。刚才说到,无论是目录还是文件,在Linux中我们都认为是广义的“文件”,为了方便阐述,我们把这个广义的“文件”叫做`Entry`吧,显然目录`Directory`和`File`都是一种`Entry`。如下: 13 | 14 | Entry.java 15 | 16 | public abstract class Entry { 17 | public abstract String getName(); 18 | public abstract int getSize(); 19 | public abstract Entry add(Entry entry) { 20 | throw new RuntimeException(); 21 | } 22 | public void printList() { 23 | printList(""); 24 | } 25 | public abstract void printList(String prefix); 26 | 27 | @Override 28 | public String toString() { 29 | return getName() + "(" + getSize() + ")"; 30 | } 31 | } 32 | 33 | File.java 34 | 35 | public class File extends Entry { 36 | private String name; 37 | private int size; 38 | 39 | public File(String name, int size) { 40 | this.name = name; 41 | this.size = size; 42 | } 43 | 44 | public String getName() { 45 | return this.name; 46 | } 47 | 48 | public int getSize() { 49 | return this.size; 50 | } 51 | 52 | public void printList(String prefix) { 53 | System.out.println(prefix + "/" + this); 54 | } 55 | } 56 | 57 | Directory.java 58 | 59 | public class Directory extends Entry { 60 | private String name; 61 | private List items = new ArrayList(); 62 | 63 | public Directory(String name) { 64 | this.name = name; 65 | } 66 | 67 | public String getName() { 68 | return this.name; 69 | } 70 | 71 | public int getSize() { 72 | int size = 0; 73 | Iterator it = this.items.iterator(); 74 | while (it.hasNext()) { 75 | Entry entry = (Entry) it.next(); 76 | size += entry.getSize(); 77 | } 78 | return size; 79 | } 80 | 81 | public Entry add(Entry entry) { 82 | items.add(entry); 83 | return this; 84 | } 85 | 86 | public void printList(String prefix) { 87 | System.out.println(prefix + "/" + getName()); 88 | Iterator it = items.iterator(); 89 | while (it.hasNext()) { 90 | Entry entry = (Entry) it.next(); 91 | entry.printList(prefix + "/" + this.name); 92 | } 93 | } 94 | } 95 | 96 | 97 | 测试一下: 98 | 99 | Client.java 100 | 101 | public class Client { 102 | public static void main(String[] args) { 103 | Entry bindir = new Directory("bin"); 104 | Entry usrdir = new Directory("usr"); 105 | Entry tmpdir = new Directory("lib"); 106 | 107 | bindir 108 | .add(new File("bash", 4)) 109 | .add(new File("ls", 6)) 110 | .add(new File("ip", 8)); 111 | 112 | usrdir 113 | .add(new Directory("bin") 114 | .add(new File("top", 10)) 115 | .add(new File("ssh", 12))) 116 | .add(new Directory("local") 117 | .add(new Directory("bin") 118 | .add(new File("eclipse", 4)) 119 | .add(new File("idea", 4))) 120 | .add(new Directory("src"))); 121 | 122 | tmpdir 123 | .add(new File("test.txt", 12)); 124 | 125 | Entry rootdir = new Directory("root"); 126 | rootdir.add(bindir).add(usrdir).add(tmpdir); 127 | rootdir.printList(); 128 | } 129 | } 130 | 131 | 输出结果: 132 | 133 | /root 134 | /root/bin 135 | /root/bin/bash(4) 136 | /root/bin/ls(6) 137 | /root/bin/ip(8) 138 | /root/usr 139 | /root/usr/bin 140 | /root/usr/bin/top(10) 141 | /root/usr/bin/ssh(12) 142 | /root/usr/local 143 | /root/usr/local/bin 144 | /root/usr/local/bin/eclipse(4) 145 | /root/usr/local/bin/idea(4) 146 | /root/usr/local/src 147 | /root/lib 148 | /root/lib/test.txt(12) 149 | 150 | # 总结 151 | 152 | 合成设计模式是一种辨识度比较高,应用场景相对比较明确的设计模式,因此相对来说也比较好理解。主要特征就两条: 153 | 154 | 1. 无论是“枝干“还是”叶子“,都是树的“组成部分”,因此都有共同的抽象,而“枝干”中的list成员变量引用的也是这个共同抽象的列表。概括地说就是“整体”和“部分”具有一致性,是一种递归包含关系; 155 | 2. 通常有一个“添加方法”,类似于本例的`add()`,因为是递归操作,一视同仁,这个方法通常定义在抽象类中,默认抛出异常,以便适用“叶子”节点无法添加子节点的情况。 -------------------------------------------------------------------------------- /composite/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | composite 8 | 9 | 10 | com.getset.designpatterns 11 | get-designpatterns 12 | 1.0-SNAPSHOT 13 | ../ 14 | 15 | 16 | -------------------------------------------------------------------------------- /composite/src/main/java/com/getset/designpatterns/composite/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.composite; 2 | 3 | import javax.swing.text.html.parser.Entity; 4 | 5 | public class Client { 6 | public static void main(String[] args) { 7 | Entry bindir = new Directory("bin"); 8 | Entry usrdir = new Directory("usr"); 9 | Entry tmpdir = new Directory("lib"); 10 | 11 | bindir 12 | .add(new File("bash", 4)) 13 | .add(new File("ls", 6)) 14 | .add(new File("ip", 8)); 15 | 16 | usrdir 17 | .add(new Directory("bin") 18 | .add(new File("top", 10)) 19 | .add(new File("ssh", 12))) 20 | .add(new Directory("local") 21 | .add(new Directory("bin") 22 | .add(new File("eclipse", 4)) 23 | .add(new File("idea", 4))) 24 | .add(new Directory("src"))); 25 | 26 | tmpdir 27 | .add(new File("test.txt", 12)); 28 | 29 | Entry rootdir = new Directory("root"); 30 | rootdir.add(bindir).add(usrdir).add(tmpdir); 31 | rootdir.printList(); 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /composite/src/main/java/com/getset/designpatterns/composite/Directory.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.composite; 2 | 3 | import com.sun.xml.internal.ws.util.QNameMap; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | 9 | public class Directory extends Entry { 10 | private String name; 11 | private List items = new ArrayList(); 12 | 13 | public Directory(String name) { 14 | this.name = name; 15 | } 16 | 17 | public String getName() { 18 | return this.name; 19 | } 20 | 21 | public int getSize() { 22 | int size = 0; 23 | Iterator it = this.items.iterator(); 24 | while (it.hasNext()) { 25 | Entry entry = (Entry) it.next(); 26 | size += entry.getSize(); 27 | } 28 | return size; 29 | } 30 | 31 | public Entry add(Entry entry) { 32 | items.add(entry); 33 | return this; 34 | } 35 | 36 | public void printList(String prefix) { 37 | System.out.println(prefix + "/" + getName()); 38 | Iterator it = items.iterator(); 39 | while (it.hasNext()) { 40 | Entry entry = (Entry) it.next(); 41 | entry.printList(prefix + "/" + this.name); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /composite/src/main/java/com/getset/designpatterns/composite/Entry.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.composite; 2 | 3 | public abstract class Entry { 4 | public abstract String getName(); 5 | public abstract int getSize(); 6 | public Entry add(Entry entry) { 7 | throw new RuntimeException(); 8 | } 9 | public void printList() { 10 | printList(""); 11 | } 12 | public abstract void printList(String prefix); 13 | 14 | @Override 15 | public String toString() { 16 | return getName() + "(" + getSize() + ")"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /composite/src/main/java/com/getset/designpatterns/composite/EntryAddException.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.composite; 2 | 3 | public class EntryAddException extends RuntimeException { 4 | public EntryAddException() { 5 | } 6 | 7 | public EntryAddException(String message) { 8 | super(message); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /composite/src/main/java/com/getset/designpatterns/composite/File.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.composite; 2 | 3 | public class File extends Entry { 4 | private String name; 5 | private int size; 6 | 7 | public File(String name, int size) { 8 | this.name = name; 9 | this.size = size; 10 | } 11 | 12 | public String getName() { 13 | return this.name; 14 | } 15 | 16 | public int getSize() { 17 | return this.size; 18 | } 19 | 20 | public void printList(String prefix) { 21 | System.out.println(prefix + "/" + this); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /decorator/README.md: -------------------------------------------------------------------------------- 1 | 装饰器模式(Decorator Pattern)以客户端透明的方式扩展对象的功能。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装,是继承关系的一个替代方案。 2 | 3 | 说到装饰者模式,估计大家都不陌生,Java I/O的设计就是采用了装饰者模式。想必初学Java I/O的时候大家都经历过一段“懵逼”期,各种`InputStream`和`OutputStream`层层嵌套,感觉就像洋葱,如果给装饰者一个形象化的吉祥物,想必非洋葱莫属。 4 | 5 | # 例子 6 | 7 | 这里装饰者的例子就直接拿来主义了,因为《图解设计模式》这本书中的例子比较形象。 8 | 9 | 这个例子的功能是给文字增加装饰边框。通过不同的装饰类实现不同的边框效果。 10 | 11 | 比如,我有个字符串“Hello, world!”,我想在两侧加上边框,那么就显示为 12 | 13 | |Hello, world!| 14 | 15 | 如果我想在上下左右均加上边框,那么就显示为 16 | 17 | +-------------+ 18 | |Hello, world!| 19 | +-------------+ 20 | 21 | 并且可以一层层嵌套: 22 | 23 | +---------------+ 24 | |+-------------+| 25 | ||Hello, world!|| 26 | |+-------------+| 27 | +---------------+ 28 | 29 | 30 | 怎么样,是不是很像洋葱啊? 31 | 32 | 总体来说,还是字符显示的问题,通过一个抽象类`Display`来定义。 33 | 34 | Display.java 35 | 36 | public abstract class Display { 37 | public abstract int getColumn(); 38 | public abstract int getRows(); 39 | public abstract String getRowText(int row); 40 | public final void show() { 41 | for (int i = 0; i < getRows(); i++) { 42 | System.out.println(getRowText(i)); 43 | } 44 | } 45 | } 46 | 47 | 其中`show()`方法将所有行的内容显示出来。那么对于没有任何边框的文字显示来说: 48 | 49 | StringDisplay.java 50 | 51 | public class StringDisplay extends Display { 52 | private String string; 53 | 54 | public StringDisplay(String string) { 55 | this.string = string; 56 | } 57 | 58 | public int getColumn() { 59 | return string.getBytes().length; 60 | } 61 | 62 | public int getRows() { 63 | return 1; 64 | } 65 | 66 | public String getRowText(int row) { 67 | if (row == 0) { 68 | return string; 69 | } else { 70 | return null; 71 | } 72 | } 73 | } 74 | 75 | 因为只有一行,所以`getRows()`返回1。我们再来看一下边框装饰后的文本: 76 | 77 | BoardStringDisplay.java 78 | 79 | public abstract class BoardStringDisplay extends Display { 80 | protected Display display; 81 | protected BoardStringDisplay(Display display) { 82 | this.display = display; 83 | } 84 | } 85 | 86 | SideBoardStringDisplay.java 87 | 88 | public class SideBoardStringDisplay extends BoardStringDisplay { 89 | 90 | protected SideBoardStringDisplay(Display display) { 91 | super(display); 92 | } 93 | 94 | public int getColumns() { 95 | return 1 + display.getColumns() + 1; // 文字两侧各增加一个字符 96 | } 97 | 98 | public int getRows() { 99 | return display.getRows(); // 行数不变 100 | } 101 | 102 | public String getRowText(int row) { 103 | return "|" + display.getRowText(row) + "|"; 104 | } 105 | } 106 | 107 | FullBoardStringDisplay.java 108 | 109 | public class FullBoardStringDisplay extends BoardStringDisplay { 110 | 111 | protected FullBoardStringDisplay(Display display) { 112 | super(display); 113 | } 114 | 115 | public int getColumns() { 116 | return 1 + display.getColumns() + 1; 117 | } 118 | 119 | public int getRows() { 120 | return 1 + display.getRows() + 1; 121 | } 122 | 123 | public String getRowText(int row) { 124 | if (row == 0 || row == display.getRows() + 1) { 125 | return "+" + makeLine('-', display.getColumns()) + "+"; 126 | } else { 127 | return "|" + display.getRowText(row - 1) + "|"; 128 | } 129 | } 130 | 131 | private String makeLine(char ch, int count) { 132 | StringBuffer buf = new StringBuffer(); 133 | for (int i = 0; i < count; i++) { 134 | buf.append(ch); 135 | } 136 | return buf.toString(); 137 | } 138 | } 139 | 140 | 试一下洋葱效果: 141 | 142 | Client.java 143 | 144 | public class Client { 145 | public static void main(String[] args) { 146 | Display d1 = new StringDisplay("Hello, world!"); 147 | Display d2 = new SideBoardStringDisplay(d1); 148 | Display d3 = new FullBoardStringDisplay( 149 | new SideBoardStringDisplay( 150 | new FullBoardStringDisplay(d2))); 151 | System.out.println("显示字符串>>>>>>"); 152 | d1.show(); 153 | System.out.println("\n增加两侧边框>>>>>>"); 154 | d2.show(); 155 | System.out.println("\n再增加全边框、两侧边框、全边框>>>>>>"); 156 | d3.show(); 157 | } 158 | } 159 | 160 | 输入如下: 161 | 162 | ![输出效果](images/result.png) 163 | 164 | # 总结 165 | 166 | 这个例子的代码量比以前的多一些,但是思路并不复杂。 167 | 168 | ![装饰器模式类图](images/decorator.png) 169 | 170 | 这里,`StringDisplay`是被装饰者,`SideBoardStringDisplay`和`FullBoardStringDisplay`是装饰器,同时也能够被装饰,因为说到底,它们都是继承自`Display`,所以可以层层嵌套,不断增强。 171 | 172 | 我们再回头看装饰器模式的特点: 173 | 1. **接口透明,在不改变被装饰者的前提下增加功能**。无论是装饰器还是被装饰者,都有共同的抽象,也许是继承同一个抽象类,也许是实现同一个接口;如此一来,装饰前后的对象都是对外提供同样的服务。就像生日蛋糕,无论是装饰了草莓、还是慕斯、还是巧克力,都还是蛋糕,不会变成一块披萨。 174 | 2. **使用了委托**装饰器将被装饰的对象作为成员,使得类之间称为弱关联关系,这一点和桥接模式的出发点是一致的。草莓点缀在生日蛋糕上是草莓生日蛋糕,点缀在披萨上是水果披萨,客人不能抢了主人风头。这里和代理模式有些类似,主要区别在于应用场景和目的,装饰器模式应当为所装饰的对象提供增强功能,而代理模式对被代理的对象施加控制,并不提供对对象本身的增强。 175 | 176 | 各种装饰器并非一定要有一个抽象(本例中的`BoardStringDisplay`),直接装饰`StringDisplay`也是OK的,这个并不是装饰器模式的特点,只是具体使用时看是否有进一步抽象的需要。 -------------------------------------------------------------------------------- /decorator/images/decorator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/decorator/images/decorator.png -------------------------------------------------------------------------------- /decorator/images/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/decorator/images/result.png -------------------------------------------------------------------------------- /decorator/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | decorator 8 | 9 | 10 | com.getset.designpatterns 11 | get-designpatterns 12 | 1.0-SNAPSHOT 13 | ../ 14 | 15 | 16 | -------------------------------------------------------------------------------- /decorator/src/main/java/com/getset/designpatterns/decorator/BoardStringDisplay.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.decorator; 2 | 3 | public abstract class BoardStringDisplay extends Display { 4 | protected Display display; 5 | protected BoardStringDisplay(Display display) { 6 | this.display = display; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /decorator/src/main/java/com/getset/designpatterns/decorator/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.decorator; 2 | 3 | public class Client { 4 | public static void main(String[] args) { 5 | Display d1 = new StringDisplay("Hello, world!"); 6 | Display d2 = new SideBoardStringDisplay(d1); 7 | Display d3 = new FullBoardStringDisplay(new SideBoardStringDisplay(new FullBoardStringDisplay(d2))); 8 | System.out.println("显示字符串>>>>>>"); 9 | d1.show(); 10 | System.out.println("\n增加两侧边框>>>>>>"); 11 | d2.show(); 12 | System.out.println("\n再增加全边框、两侧边框、全边框>>>>>>"); 13 | d3.show(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /decorator/src/main/java/com/getset/designpatterns/decorator/Display.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.decorator; 2 | 3 | public abstract class Display { 4 | public abstract int getColumns(); 5 | public abstract int getRows(); 6 | public abstract String getRowText(int row); 7 | public final void show() { 8 | for (int i = 0; i < getRows(); i++) { 9 | System.out.println(getRowText(i)); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /decorator/src/main/java/com/getset/designpatterns/decorator/FullBoardStringDisplay.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.decorator; 2 | 3 | public class FullBoardStringDisplay extends BoardStringDisplay { 4 | 5 | protected FullBoardStringDisplay(Display display) { 6 | super(display); 7 | } 8 | 9 | public int getColumns() { 10 | return 1 + display.getColumns() + 1; 11 | } 12 | 13 | public int getRows() { 14 | return 1 + display.getRows() + 1; 15 | } 16 | 17 | public String getRowText(int row) { 18 | if (row == 0 || row == display.getRows() + 1) { 19 | return "+" + makeLine('-', display.getColumns()) + "+"; 20 | } else { 21 | return "|" + display.getRowText(row - 1) + "|"; 22 | } 23 | } 24 | 25 | private String makeLine(char ch, int count) { 26 | StringBuffer buf = new StringBuffer(); 27 | for (int i = 0; i < count; i++) { 28 | buf.append(ch); 29 | } 30 | return buf.toString(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /decorator/src/main/java/com/getset/designpatterns/decorator/SideBoardStringDisplay.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.decorator; 2 | 3 | public class SideBoardStringDisplay extends BoardStringDisplay { 4 | 5 | protected SideBoardStringDisplay(Display display) { 6 | super(display); 7 | } 8 | 9 | public int getColumns() { 10 | return 1 + display.getColumns() + 1; // 文字两侧各增加一个字符 11 | } 12 | 13 | public int getRows() { 14 | return display.getRows(); // 行数不变 15 | } 16 | 17 | public String getRowText(int row) { 18 | return "|" + display.getRowText(row) + "|"; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /decorator/src/main/java/com/getset/designpatterns/decorator/StringDisplay.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.decorator; 2 | 3 | public class StringDisplay extends Display { 4 | private String string; 5 | 6 | public StringDisplay(String string) { 7 | this.string = string; 8 | } 9 | 10 | public int getColumns() { 11 | return string.getBytes().length; 12 | } 13 | 14 | public int getRows() { 15 | return 1; 16 | } 17 | 18 | public String getRowText(int row) { 19 | if (row == 0) { 20 | return string; 21 | } else { 22 | return null; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /facade/README.md: -------------------------------------------------------------------------------- 1 | > 本文源码见:https://github.com/get-set/get-designpatterns/tree/master/facade 2 | 3 | 门面模式(Facade Pattern)用于隐藏系统的复杂性,并向客户端提供一些简化访问方法和对现有系统类方法的委托调用。这种类型的设计模式属于结构型模式,用来隐藏系统的复杂性。 4 | 5 | 现在政府办事越来越方便了,很多城市、区县都有统一的办事大厅,里边有各个部门的窗口,一般进去一圈该办的事情就齐活了。想想之前,政府办个事情,这个部门盖个章,那个部门开个证明,不同的部门分散在城市的不同位置,一天有时候都办不妥当。先为这种进步点个赞! 6 | 7 | 各个部门还在各自的地方,但是都会在办事大厅开一个窗口。其实这个办事大厅就相当于各个部门的一个总的门面,就是用到了“门面模式”。我们现在的系统通常会越来越复杂,类之间的依赖调用关系逐渐变复杂,接口越来越多,这时候通常会用一个专门的类对某个模块或子系统进行一个包装,归置归置,简化对外提供服务的方法调用,较少方法数量。 8 | 9 | # 例子 10 | 11 | 好久没有用那个画图的例子了,这里再次搬出来。对于门面模式来说,没有固化的类关系模型,一切以简化和封装为出发点,所以这个例子也比较简洁,领会意思即可。 12 | 13 | 这个例子中,有圆形、矩形和三角形三种形状,它们都继承自`Shape`: 14 | 15 | # Shape.java 16 | public interface Shape { 17 | void draw(); 18 | } 19 | 20 | # Circle.java 21 | public class Circle implements Shape { 22 | public void draw() { 23 | System.out.println("Draw a circle."); 24 | } 25 | } 26 | 27 | # Rectangle.java 28 | public class Rectangle implements Shape { 29 | public void draw() { 30 | System.out.println("Draw a ectangle."); 31 | } 32 | } 33 | 34 | # Triangle.java 35 | public class Triangle implements Shape{ 36 | public void draw() { 37 | System.out.println("Draw a triangle."); 38 | } 39 | } 40 | 41 | 随着形状的增多,类数量也会显著增加,这个时候如果有一个统一的类来提供各个图形的`draw`功能就好了。我们之前用这个例子解释过工厂模式,工厂模式主要是用来获取类实例的,而门面模式是统一对外提供接口调用的。本例的门面类如下: 42 | 43 | ShapeDrawer.java 44 | 45 | public class ShapeDrawer { 46 | private ShapeDrawer() {} 47 | public static void drawCircle() { 48 | new Circle().draw(); 49 | } 50 | public static void drawTrangle() { 51 | new Triangle().draw(); 52 | } 53 | public static void drawRectangle() { 54 | new Rectangle().draw(); 55 | } 56 | } 57 | 58 | 这里,`ShapeDrawer`是作为类来使用了,因为其方法都是`static`的,因此也就不用创建其对象,所以构造方法声明为私有的。使用时直接调用静态方法即可:`ShapeDrawer.drawCircle();`。 59 | 60 | 当然,这只是一种方式,并不是说门面模式就要这样做。 61 | * 门面也可以是单例的,由一个单一的对象负责提供服务; 62 | * 门面也可以是普通的多例的,比如其要包装的目标是多个对象,那么每个对象可能会需要一个门面实例,这个时候,门面通常也是起到一种隔离的作用,比如被包装的对象有许多接口是系统内调用的,并不对其他模块或子系统开放,而门面对象起到了一层代理的作用。 63 | 64 | # 总结 65 | 66 | 门面模式更多是一种设计思想,而不是具体模型。目的是为子系统中的一组接口提供一个一致的界面,通过定义一个高层接口,降低访问复杂系统的内部子系统时的复杂度,使得这一子系统更加容易使用。 -------------------------------------------------------------------------------- /facade/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | facade 8 | 9 | 10 | com.getset.designpatterns 11 | get-designpatterns 12 | 1.0-SNAPSHOT 13 | ../ 14 | 15 | 16 | -------------------------------------------------------------------------------- /facade/src/main/java/com/getset/designpatterns/facade/Circle.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.facade; 2 | 3 | public class Circle implements Shape { 4 | public void draw() { 5 | System.out.println("Draw a circle."); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /facade/src/main/java/com/getset/designpatterns/facade/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.facade; 2 | 3 | public class Client { 4 | public static void main(String[] args) { 5 | ShapeDrawer.drawCircle(); 6 | ShapeDrawer.drawRectangle(); 7 | ShapeDrawer.drawTrangle(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /facade/src/main/java/com/getset/designpatterns/facade/Rectangle.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.facade; 2 | 3 | public class Rectangle implements Shape { 4 | public void draw() { 5 | System.out.println("Draw a ectangle."); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /facade/src/main/java/com/getset/designpatterns/facade/Shape.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.facade; 2 | 3 | public interface Shape { 4 | void draw(); 5 | } 6 | -------------------------------------------------------------------------------- /facade/src/main/java/com/getset/designpatterns/facade/ShapeDrawer.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.facade; 2 | 3 | public class ShapeDrawer { 4 | private ShapeDrawer() {} 5 | public static void drawCircle() { 6 | new Circle().draw(); 7 | } 8 | public static void drawTrangle() { 9 | new Triangle().draw(); 10 | } 11 | public static void drawRectangle() { 12 | new Rectangle().draw(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /facade/src/main/java/com/getset/designpatterns/facade/Triangle.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.facade; 2 | 3 | public class Triangle implements Shape{ 4 | public void draw() { 5 | System.out.println("Draw a triangle."); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /factory-method/README.md: -------------------------------------------------------------------------------- 1 | 工厂方法模式同 [简单工厂模式](../simple-factory) 一样,也是创建类模式,又叫做虚拟构造(Virtual Constructor)模式或多态工厂(Polymorphic Factory)模式。其用意是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中。 2 | 3 | 上篇说到, [简单工厂模式](../simple-factory) 并未做到完全的“开闭原则”。回顾一下,“开”即对扩展开放,这点是没错的, [简单工厂模式](../simple-factory) 的初衷之一就是方便增加“产品类型”的时候;“闭”即对修改关闭,这点其实并未做到,当需要增删改“产品类型”的时候,工厂类必须要修改,因为工厂类“知道”如何创建所有的产品类型的对象。 4 | 5 | 那么工厂方法模式就是为了完全满足“开闭原则”,即在 [简单工厂模式](../simple-factory) 的基础上,做到当增加产品类型的时候,无需改动现有的代码。继续用上篇的例子: 6 | 7 | # 例子 8 | 9 | 仍然是做一个画图软件,可以画矩形、三角形和圆形等,每一种图形都用一个类来管理: 10 | * `Rectangle` 11 | * `Circle` 12 | * `Triangle` 13 | 每个类都有各自的`draw()`方法,共同实现`Shape`接口。 14 | 15 | Shape.java 16 | 17 | public interface Shape { 18 | void draw(); 19 | } 20 | 21 | 22 | Rectangle.java 23 | 24 | public class Rectangle implements Shape { 25 | @Override 26 | public void draw() { 27 | System.out.println("Draw a rectangle."); 28 | } 29 | } 30 | 31 | 32 | Triangle.java 33 | 34 | public class Triangle implements Shape { 35 | @Override 36 | public void draw() { 37 | System.out.println("Draw a triangle."); 38 | } 39 | } 40 | 41 | 42 | Circle.java 43 | 44 | public class Circle implements Shape { 45 | @Override 46 | public void draw() { 47 | System.out.println("Draw a circle."); 48 | } 49 | } 50 | 51 | 以上几个类都没有变化,有变化的是工厂类,工厂类也采用基于接口的设计,由不同的具体工厂类负责相应对象的创建: 52 | 53 | ShapeFactory.java 54 | 55 | public interface ShapeFactory { 56 | Shape getShape(); 57 | } 58 | 59 | RectangleFactory.java 60 | 61 | public class RectangleFactory implements ShapeFactory { 62 | public Shape getShape() { 63 | return new Rectangle(); 64 | } 65 | } 66 | 67 | CircleFactory.java 68 | 69 | public class CircleFactory implements ShapeFactory{ 70 | public Shape getShape() { 71 | return new Circle(); 72 | } 73 | } 74 | 75 | Triangle.Factory.java 76 | 77 | public class CircleFactory implements ShapeFactory{ 78 | public Shape getShape() { 79 | return new Circle(); 80 | } 81 | } 82 | 83 | 那么在需要某个形状的时候,就通过相应的具体工厂类创建即可: 84 | 85 | Client.java 86 | 87 | public class Client { 88 | public static void main(String[] args) { 89 | ShapeFactory factory = new CircleFactory(); 90 | Shape c = factory.getShape(); 91 | c.draw(); 92 | } 93 | } 94 | 95 | 再来看一下类图: 96 | 97 | ![](images/factorymethod.png) 98 | 99 | 与 [简单工厂模式](../simple-factory) 对比以下: 100 | 101 | ![](../simple-factory/images/simplefactory1.png) 102 | 103 | 区别就在于 [简单工厂模式](../simple-factory) 下的一个具体的工厂类转换成了基于接口`ShapeFactory`的三个具体工厂类。 104 | 105 | 好处也是明显的:当增加一个新的形状类型的时候,不需要对现有代码做任何更改,增加一个相应的实现了`ShapeFactory`的具体工厂类即可。可见,工厂方法模式能够完全做到“开闭原则”。 106 | 107 | 这个例子仍然是不恰当的,因为无论如何这一套模式设计比原始的实现方式有更加复杂了。所以再次赘述一遍注意事项,以上例子是为了说明工厂方法模式,但并不是一个合理的应用。**复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。** 108 | 109 | # Java中的应用 110 | 下边看一下合理的应用场景是如何的,在Java中的实际应用的例子(来自《Java与模式》): 111 | 112 | ** 1. Java聚集中的应用 ** 113 | 114 | Java聚集是一套设计精良的数据结构实现,主要的Java聚集都实现自`java.util.Collection`接口,这个接口的父接口`Iterable`规定所有的Java聚集都必须提供一个`iterator()`方法,返还一个`Iterator`类型的对象: 115 | 116 | java.lang.Iterable.java 117 | 118 | public interface Iterable { 119 | ... ... 120 | Iterator iterator(); 121 | ... ... 122 | } 123 | 124 | ArrayLis是我们常用的一个Collection实现类,其iterator()方法实现如下: 125 | 126 | java.util.ArrayList.java 127 | 128 | public class ArrayList extends AbstractList 129 | implements List, RandomAccess, Cloneable, java.io.Serializable { 130 | ... ... 131 | // 实现iterato接口,返回一个Iterator对象 132 | public Iterator iterator() { 133 | return new Itr(); 134 | } 135 | private class Itr implements Iterator { 136 | ... ... 137 | } 138 | ... ... 139 | } 140 | 141 | 可见,`ArrayList`类的`iterator()`方法就是一个具体工厂类的工厂方法,而`Collection`就是一个抽象工厂。除了`ArrayList`还有`LinkedList`等等具体实现类。 142 | 143 | # 总结 144 | 145 | 从上边的例子可以看到,工厂方法模式其实是将“面向接口”编程的思路应用在了工厂类上,这样有几个方便的地方: 146 | 147 | 1. 做到了完全的“开闭原则”,因为增加新的“产品”和相应的“工厂”均不需修改现有代码; 148 | 2. 工厂设计模式通常应用在复杂的对象创建场景中,因此面临多层的继承关系,比如`ArrayList`实现了`List`接口,而后者继承自`Collect`接口,`Collect`又继承自`Iterator`接口。有时候具体工厂方法与具体产品是有层次对应关系的,比如: 149 | 150 | ![](images/hiera-factory-method.png) 151 | 152 | 这种情况也是只有一个工厂类的 [简单工厂模式](../simple-factory) 所无法满足的。 153 | -------------------------------------------------------------------------------- /factory-method/images/factorymethod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/factory-method/images/factorymethod.png -------------------------------------------------------------------------------- /factory-method/images/hiera-factory-method.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/factory-method/images/hiera-factory-method.png -------------------------------------------------------------------------------- /factory-method/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 4.0.0 7 | factory-method 8 | 9 | 10 | com.getset.designpatterns 11 | get-designpatterns 12 | 1.0-SNAPSHOT 13 | ../ 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /factory-method/src/main/java/com/getset/designpatterns/factorymethod1/Circle.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.factorymethod1; 2 | 3 | public class Circle implements Shape { 4 | public void draw() { 5 | System.out.println("Draw a circle."); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /factory-method/src/main/java/com/getset/designpatterns/factorymethod1/CircleFactory.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.factorymethod1; 2 | 3 | public class CircleFactory implements ShapeFactory{ 4 | public Shape getShape() { 5 | return new Circle(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /factory-method/src/main/java/com/getset/designpatterns/factorymethod1/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.factorymethod1; 2 | 3 | public class Client { 4 | public static void main(String[] args) { 5 | ShapeFactory factory = new CircleFactory(); 6 | Shape c = factory.getShape(); 7 | c.draw(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /factory-method/src/main/java/com/getset/designpatterns/factorymethod1/Rectangle.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.factorymethod1; 2 | 3 | public class Rectangle implements Shape { 4 | public void draw() { 5 | System.out.println("Draw a ectangle."); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /factory-method/src/main/java/com/getset/designpatterns/factorymethod1/RectangleFactory.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.factorymethod1; 2 | 3 | public class RectangleFactory implements ShapeFactory { 4 | public Shape getShape() { 5 | return new Rectangle(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /factory-method/src/main/java/com/getset/designpatterns/factorymethod1/Shape.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.factorymethod1; 2 | 3 | public interface Shape { 4 | void draw(); 5 | } 6 | -------------------------------------------------------------------------------- /factory-method/src/main/java/com/getset/designpatterns/factorymethod1/ShapeFactory.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.factorymethod1; 2 | 3 | public interface ShapeFactory { 4 | Shape getShape(); 5 | } 6 | -------------------------------------------------------------------------------- /factory-method/src/main/java/com/getset/designpatterns/factorymethod1/Triangle.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.factorymethod1; 2 | 3 | public class Triangle implements Shape { 4 | public void draw() { 5 | System.out.println("Draw a triangle."); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /factory-method/src/main/java/com/getset/designpatterns/factorymethod1/TriangleFactory.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.factorymethod1; 2 | 3 | public class TriangleFactory implements ShapeFactory { 4 | public Shape getShape() { 5 | return new Triangle(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /factory-method/src/main/java/com/getset/designpatterns/hierafactorymethod/AbstractFactory.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.hierafactorymethod; 2 | 3 | public interface AbstractFactory { 4 | AbstractProduct create(); 5 | } 6 | -------------------------------------------------------------------------------- /factory-method/src/main/java/com/getset/designpatterns/hierafactorymethod/AbstractProduct.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.hierafactorymethod; 2 | 3 | public interface AbstractProduct { 4 | } 5 | -------------------------------------------------------------------------------- /factory-method/src/main/java/com/getset/designpatterns/hierafactorymethod/AbstractSubFactory.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.hierafactorymethod; 2 | 3 | public interface AbstractSubFactory extends AbstractFactory { 4 | } 5 | -------------------------------------------------------------------------------- /factory-method/src/main/java/com/getset/designpatterns/hierafactorymethod/AbstractSubProduct.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.hierafactorymethod; 2 | 3 | public interface AbstractSubProduct extends AbstractProduct { 4 | } 5 | -------------------------------------------------------------------------------- /factory-method/src/main/java/com/getset/designpatterns/hierafactorymethod/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.hierafactorymethod; 2 | 3 | public class Client { 4 | public static void main(String[] args) { 5 | AbstractFactory factory = new ConcreteSubFactory(); 6 | AbstractProduct product = factory.create(); 7 | System.out.println(product.getClass().getCanonicalName()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /factory-method/src/main/java/com/getset/designpatterns/hierafactorymethod/ConcreteFactory.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.hierafactorymethod; 2 | 3 | public class ConcreteFactory implements AbstractFactory { 4 | public AbstractProduct create() { 5 | return new ConcreteProduct(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /factory-method/src/main/java/com/getset/designpatterns/hierafactorymethod/ConcreteProduct.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.hierafactorymethod; 2 | 3 | public class ConcreteProduct implements AbstractProduct { 4 | } 5 | -------------------------------------------------------------------------------- /factory-method/src/main/java/com/getset/designpatterns/hierafactorymethod/ConcreteSubFactory.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.hierafactorymethod; 2 | 3 | public class ConcreteSubFactory implements AbstractSubFactory { 4 | public AbstractProduct create() { 5 | return new ConcreteSubProduct(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /factory-method/src/main/java/com/getset/designpatterns/hierafactorymethod/ConcreteSubProduct.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.hierafactorymethod; 2 | 3 | public class ConcreteSubProduct implements AbstractSubProduct { 4 | } 5 | -------------------------------------------------------------------------------- /flyweight/images/flyweight-font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/flyweight/images/flyweight-font.png -------------------------------------------------------------------------------- /flyweight/images/flyweight-lifeexample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/flyweight/images/flyweight-lifeexample.jpg -------------------------------------------------------------------------------- /flyweight/images/flyweight-output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/flyweight/images/flyweight-output.png -------------------------------------------------------------------------------- /flyweight/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | flyweight 8 | 9 | 10 | com.getset.designpatterns 11 | get-designpatterns 12 | 1.0-SNAPSHOT 13 | ../ 14 | 15 | 16 | -------------------------------------------------------------------------------- /flyweight/src/main/java/com/getset/designpatterns/flyweight/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.flyweight; 2 | 3 | public class Client { 4 | public static void main(String[] args) { 5 | new NumsPrinter("18610861680").print(); 6 | 7 | // String的字符串池 8 | String a = "abc"; 9 | String b = "abc"; 10 | String c = new String("abc"); 11 | System.out.println(a == b); 12 | System.out.println(c == b); 13 | // Integer中缓存的使用 14 | System.out.println(Integer.valueOf(123) == Integer.valueOf(123)); 15 | System.out.println(Integer.valueOf(1234) == Integer.valueOf(1234)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /flyweight/src/main/java/com/getset/designpatterns/flyweight/NumSeal.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.flyweight; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | 7 | public class NumSeal { 8 | private Integer num; 9 | 10 | private String[] fontLines; 11 | 12 | // 构造方法中读取字体信息 13 | public NumSeal(Integer num) { 14 | this.num = num; 15 | this.fontLines = new String[10]; 16 | try { 17 | BufferedReader reader = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("/fonts/seal-" + num + ".txt"))); 18 | String line; 19 | for (int i = 0; i < 10; i++) { 20 | line = reader.readLine(); 21 | if (line != null) { 22 | fontLines[i] = line; 23 | } else { 24 | break; 25 | } 26 | } 27 | reader.close(); 28 | } catch (IOException e) { 29 | e.printStackTrace(); 30 | } 31 | } 32 | 33 | // 按行打印,共10行10列靠左打印 34 | public void sealPrint(int line) { 35 | System.out.printf(" %-10.10s", fontLines[line]); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /flyweight/src/main/java/com/getset/designpatterns/flyweight/NumSealFactory.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.flyweight; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class NumSealFactory { 7 | private Map seals = new HashMap(10); 8 | public NumSeal getSeal(Integer num) { 9 | NumSeal seal = seals.get(num); 10 | if (seal == null) { 11 | seal = new NumSeal(num); 12 | System.out.println("制作一个数字为" + num + "的印章"); 13 | seals.put(num, seal); 14 | return seal; 15 | } 16 | return seal; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /flyweight/src/main/java/com/getset/designpatterns/flyweight/NumsPrinter.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.flyweight; 2 | 3 | public class NumsPrinter { 4 | private NumSeal[] seals; 5 | 6 | public NumsPrinter(String nums) { 7 | seals = new NumSeal[nums.length()]; 8 | NumSealFactory numSealFactory = new NumSealFactory(); 9 | for (int i = 0; i < nums.length(); i++) { 10 | seals[i] = numSealFactory.getSeal(nums.charAt(i) - 48); 11 | } 12 | } 13 | 14 | public void print() { 15 | for (int l = 0; l < 10; l++) { 16 | for (NumSeal seal:seals) { 17 | seal.sealPrint(l); 18 | } 19 | System.out.println(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /flyweight/src/main/resources/fonts/seal-0.txt: -------------------------------------------------------------------------------- 1 | 2 | $$$$$$\ 3 | $$$ __$$\ 4 | $$$$\ $$ | 5 | $$\$$\$$ | 6 | $$ \$$$$ | 7 | $$ |\$$$ | 8 | \$$$$$$ / 9 | \______/ 10 | 11 | -------------------------------------------------------------------------------- /flyweight/src/main/resources/fonts/seal-1.txt: -------------------------------------------------------------------------------- 1 | 2 | $$\ 3 | $$$$ | 4 | \_$$ | 5 | $$ | 6 | $$ | 7 | $$ | 8 | $$$$$$\ 9 | \______| 10 | 11 | -------------------------------------------------------------------------------- /flyweight/src/main/resources/fonts/seal-2.txt: -------------------------------------------------------------------------------- 1 | 2 | $$$$$$\ 3 | $$ __$$\ 4 | \__/ $$ | 5 | $$$$$$ | 6 | $$ ____/ 7 | $$ | 8 | $$$$$$$$\ 9 | \________| 10 | 11 | -------------------------------------------------------------------------------- /flyweight/src/main/resources/fonts/seal-3.txt: -------------------------------------------------------------------------------- 1 | 2 | $$$$$$\ 3 | $$ ___$$\ 4 | \_/ $$ | 5 | $$$$$ / 6 | \___$$\ 7 | $$\ $$ | 8 | \$$$$$$ | 9 | \______/ 10 | 11 | -------------------------------------------------------------------------------- /flyweight/src/main/resources/fonts/seal-4.txt: -------------------------------------------------------------------------------- 1 | 2 | $$\ $$\ 3 | $$ | $$ | 4 | $$ | $$ | 5 | $$$$$$$$ | 6 | \_____$$ | 7 | $$ | 8 | $$ | 9 | \__| 10 | 11 | -------------------------------------------------------------------------------- /flyweight/src/main/resources/fonts/seal-5.txt: -------------------------------------------------------------------------------- 1 | 2 | $$$$$$$\ 3 | $$ ____| 4 | $$ | 5 | $$$$$$$\ 6 | \_____$$\ 7 | $$\ $$ | 8 | \$$$$$$ | 9 | \______/ 10 | 11 | -------------------------------------------------------------------------------- /flyweight/src/main/resources/fonts/seal-6.txt: -------------------------------------------------------------------------------- 1 | 2 | $$$$$$\ 3 | $$ __$$\ 4 | $$ / \__| 5 | $$$$$$$\ 6 | $$ __$$\ 7 | $$ / $$ | 8 | $$$$$$ | 9 | \______/ 10 | 11 | -------------------------------------------------------------------------------- /flyweight/src/main/resources/fonts/seal-7.txt: -------------------------------------------------------------------------------- 1 | 2 | $$$$$$$$\ 3 | \____$$ | 4 | $$ / 5 | $$ / 6 | $$ / 7 | $$ / 8 | $$ / 9 | \__/ 10 | 11 | -------------------------------------------------------------------------------- /flyweight/src/main/resources/fonts/seal-8.txt: -------------------------------------------------------------------------------- 1 | 2 | $$$$$$\ 3 | $$ __$$\ 4 | $$ / $$ | 5 | $$$$$$ | 6 | $$ __$$< 7 | $$ / $$ | 8 | \$$$$$$ | 9 | \______/ 10 | 11 | -------------------------------------------------------------------------------- /flyweight/src/main/resources/fonts/seal-9.txt: -------------------------------------------------------------------------------- 1 | 2 | $$$$$$\ 3 | $$ __$$\ 4 | $$ / $$ | 5 | \$$$$$$$ | 6 | \____$$ | 7 | $$\ $$ | 8 | \$$$$$$ | 9 | \______/ 10 | 11 | -------------------------------------------------------------------------------- /interpreter/README.md: -------------------------------------------------------------------------------- 1 | 解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。 2 | 3 | 解释器模式在我们开发过程中并不常用,是个比较小众的设计模式。这种模式通常被用在 SQL 解析、符号处理引擎等。 4 | 5 | 什么是符号处理引擎呢?举几个例子, 6 | 7 | * 强大而又令人望而却步的正则表达式,相信大多数程序猿都接触过吧,`[0-9a-zA-Z]+`这样一串符号就能够匹配一个由数字和大小写字母组成的字符串。类似的还有XML文件的解析、SQL语句的解析等等。 8 | * 假设你做了一个机器人,可以通过指令告诉它如何执行动作,比如向前走10个单位是`forward 10`,向左转是`turn left`,当你输入`begin forward 10 turn left forward 5 end` 组成的命令给它的终端,它就能明白命令的意思(向前走10个单位然后左转走5个单位)并执行。 9 | * 在举个简单点的例子,正则表达式,我们通常用的计算器是一步一步来的,比如`2+3-4/2=`当输入`+`、`-``/`和`=`的时候都会立即响应。但是还有种计算器,可以在输入整个公式后,一并进行计算,这种计算器可以直接输入`2+3-4/2`然后回车后直接给出整个式子的结果,这种计算器就是“解释型”计算器。 10 | 11 | 就以最后一个计算式的例子来说,`2+3-4/2`可以用如下树形表示: 12 | 13 | ![语法树](images/express-tree.png) 14 | 15 | 这就相当于一个“语法树”。多数编程语言在编译源码时或直接解释执行时,通常都会生成类似这样的语法树,不过复杂得多。 16 | 17 | 在图示的这个语法树中,`2`、`3`、`4`、`2`都是叶子节点,称之为**“终结符表达式”**,因为它们已经不能再进一步展开,就像上边正则表达式的例子中的`09azAZ`。而树中的非叶子节点被称为**“非终结符表达式”**,比如`-`、`+`、`/`,当解释到它们时,还可以展开为子表达式。 18 | 19 | 对于这种树形结构,最擅长的设计模式那就是**组合模式**了,当然前提是树中的叶子节点和非叶子节点都能够抽象为一种接口。 20 | 21 | ![类关系图](images/iterpreter.png) 22 | 23 | 所以对于表达式的解析和求值来说,就是一个类似递归的过程。无论是叶子节点还是非叶子节点,只要完成自己“份内”的工作即可,那就是返回表达式的值。如果是叶子节点,那么就是它本身;如果是非叶子节点,那么就是表达式计算之后的结果。 24 | 25 | 所以对于抽象表达式来说,抽象起来就很简单了,有个`interpret()`方法就OK了,返回类型就是一个int型,逻辑就是上一段描述的样子。 26 | 27 | Expression.java 28 | 29 | public interface Expression { 30 | int interpret(); 31 | } 32 | 33 | 对于叶子节点的数字,也就是终结符表达式来说: 34 | 35 | Num.java 36 | 37 | public class Num implements Expression { 38 | private int number; 39 | 40 | public Num(int number) { 41 | this.number = number; 42 | } 43 | 44 | // 返回值就是数字本身 45 | public int interpret() { 46 | return number; 47 | } 48 | } 49 | 50 | 对于非叶子节点的运算符,也就是非终结符表达式来说,都需要一个左值和右值才能进行解析运算: 51 | 52 | Add.java 53 | 54 | public class Add implements Expression { 55 | private Expression left, right; 56 | 57 | public Add(Expression left, Expression right) { 58 | this.left = left; 59 | this.right = right; 60 | } 61 | 62 | // 返回值是两个子表达式的值相加 63 | public int interpret() { 64 | return left.interpret() + right.interpret(); 65 | } 66 | } 67 | 68 | 左值`left`和右值`right`都是`Expression`,`interpret()`方法返回值是两个子表达式的值相加。 69 | 70 | 对于减法`Sub.java`、乘法`Mul.java`和除法`Div.java`也是类似的。 71 | 72 | 当进行计算时: 73 | 74 | Client.java 75 | 76 | public class Client { 77 | public static void main(String[] args) { 78 | // 解析 2+3-4/2 的值 79 | Expression a = new Num(2), b = new Num(3), 80 | c = new Num(4), d = new Num(2); 81 | 82 | Expression result = new Sub(new Add(a, b), new Div(c, d)); 83 | 84 | System.out.println(result.interpret()); 85 | } 86 | } 87 | 88 | 通过这个例子,我们可以看出: 89 | 90 | 1. 解释器模式的本质:分离实现,解释执行。通过一个解释器对象处理一个语法规则的方式,把复杂的功能分离开,然后选择需要被执行的功能,并把这些功能组合成需要解释执行的抽象语法树,再按照抽象语法树来解释执行,实现相应的功能。从本质上看,解释器模式的思路仍然是分离、封装和简化,这与很多其他模式是一样的。 91 | 2. 对于定义好的抽象语法树,解释器来负责解释执行,而选择解释器的工作,在构建抽象语法树的时候就完成了,一般由根结点的解释器开始解释,然后递归地调用其他解释器。 92 | 93 | 94 | **解释器模式的优缺点**([引用](https://www.cnblogs.com/5iedu/p/5595153.html)) 95 | 96 | 优点: 97 | 98 | * 易于实现文法:在解释器模式中,一条语法规则用一个解释器对象来解释执行。对于解释器的实现来讲,功能就变得比较简单,只需要考虑这一条语法规则的实现就可以了,其他的都不用管。 99 | * 易于扩展新的语法。由于解释器采用类来描述语法规则,因此可以通过继承等机制创建相应的解释器对象,在创建抽象语法树的时候使用这个新的解释器对象就可以了。 100 | 101 | 缺点: 102 | 103 | * 执行效率较低。由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。 104 | * 对于复杂文法难以维护。在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护,此时可以考虑使用语法分析程序等方式来取代解释器模式。 105 | 106 | **解释器模式的应用场景** 107 | 108 | 1. 当一个语言需要解释执行,并可以将该语言中的句子表示为一个抽象语法树的时候,可以考虑使用解释器模式(如XML文档解释、正则表达式等领域) 109 | 2. 一些重复出现的问题可以用一种简单的语言来进行表达。 110 | 3. 一个语言的文法较为简单. 111 | 4. 当执行效率不是关键和主要关心的问题时可考虑解释器模式(注:高效的解释器通常不是通过直接解释抽象语法树来实现的,而是需要将它们转换成其他形式,使用解释器模式的执行效率并不高。) -------------------------------------------------------------------------------- /interpreter/images/express-tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/interpreter/images/express-tree.png -------------------------------------------------------------------------------- /interpreter/images/iterpreter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/interpreter/images/iterpreter.png -------------------------------------------------------------------------------- /interpreter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | get-designpatterns 7 | com.getset.designpatterns 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | interpreter 13 | 14 | 15 | -------------------------------------------------------------------------------- /interpreter/src/main/java/com/getset/designpatterns/interpreter/Add.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.interpreter; 2 | 3 | public class Add implements Expression { 4 | private Expression left, right; 5 | 6 | public Add(Expression left, Expression right) { 7 | this.left = left; 8 | this.right = right; 9 | } 10 | 11 | public int interpret() { 12 | return left.interpret() + right.interpret(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /interpreter/src/main/java/com/getset/designpatterns/interpreter/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.interpreter; 2 | 3 | public class Client { 4 | public static void main(String[] args) { 5 | // 解析 2+3-4/2 的值 6 | Expression a = new Num(2), b = new Num(3), 7 | c = new Num(4), d = new Num(2); 8 | 9 | Expression result = new Sub(new Add(a, b), new Div(c, d)); 10 | 11 | System.out.println(result.interpret()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /interpreter/src/main/java/com/getset/designpatterns/interpreter/Div.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.interpreter; 2 | 3 | public class Div implements Expression { 4 | private Expression left, right; 5 | 6 | public Div(Expression left, Expression right) { 7 | this.left = left; 8 | this.right = right; 9 | } 10 | 11 | public int interpret() { 12 | return left.interpret() / right.interpret(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /interpreter/src/main/java/com/getset/designpatterns/interpreter/Expression.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.interpreter; 2 | 3 | public interface Expression { 4 | int interpret(); 5 | } 6 | -------------------------------------------------------------------------------- /interpreter/src/main/java/com/getset/designpatterns/interpreter/Mul.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.interpreter; 2 | 3 | public class Mul implements Expression { 4 | private Expression left, right; 5 | 6 | public Mul(Expression left, Expression right) { 7 | this.left = left; 8 | this.right = right; 9 | } 10 | 11 | public int interpret() { 12 | return left.interpret() * right.interpret(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /interpreter/src/main/java/com/getset/designpatterns/interpreter/Num.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.interpreter; 2 | 3 | public class Num implements Expression { 4 | private int number; 5 | 6 | public Num(int number) { 7 | this.number = number; 8 | } 9 | 10 | public int interpret() { 11 | return number; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /interpreter/src/main/java/com/getset/designpatterns/interpreter/Sub.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.interpreter; 2 | 3 | public class Sub implements Expression { 4 | private Expression left, right; 5 | 6 | public Sub(Expression left, Expression right) { 7 | this.left = left; 8 | this.right = right; 9 | } 10 | 11 | public int interpret() { 12 | return left.interpret() - right.interpret(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /iterator/README.md: -------------------------------------------------------------------------------- 1 | 迭代器(Iterator)模式又叫游标(Cursor)模式,通常用于集合类型来提供一种顺序访问其中元素而又不必暴漏集合的内部结构,是一种行为模式。 2 | 3 | 关于迭代器(Iterator)我想对Java Collection有过接触的同学就不陌生,所以本文也就无需举其他例子了,看一下在Java SDK中是如何实现的就好了。 4 | 5 | 据统计,`java.util.ArrayList`是Java SDK中使用频率最高的类。有人说程序就是数据结构+算法,可见数据结构的重要性。我们在日常开发中,时常跟Java集合中的各种工具类打交道,对于它们,遍历元素又是家常便饭,比如: 6 | 7 | String[] strings = new String[]{"Hello,", "Java", "Design", "Patterns."}; 8 | List stringList = Arrays.asList(strings); 9 | Iterator iterator = stringList.iterator(); 10 | while (iterator.hasNext()) { 11 | System.out.print(iterator.next() + " "); 12 | } 13 | 14 | 输出即: 15 | 16 | Hello, Java Design Patterns. 17 | 18 | 其中的`Iterator`就是迭代器,它有两个核心方法: 19 | 20 | java.util.Iterator.java(不考虑Java8新增内容) 21 | 22 | public interface Iterator { 23 | boolean hasNext(); 24 | E next(); 25 | } 26 | 27 | * `hasNext()`用于判断是否还有下一个元素; 28 | * `next()`用于返回下一个元素,同时“看向”这个元素的再下一个元素。 29 | 30 | 我们常用的一些Java数据结构工具: 31 | 32 | ![JavaCollection](images/Collection_interfaces.png) 33 | 34 | `Collection`是继承自`Iterable`,而后者的核心方法就是返回`Iterator`实例的`iterator()`方法(不考虑Java8增加的内容的话): 35 | 36 | java.lang.Iterable.java 37 | 38 | public interface Iterable { 39 | Iterator iterator(); 40 | } 41 | 42 | 所以我们平时使用的各种不同的`List`、`Set`和`Queue`的具体实现,都能返回迭代器以便能够对它们中的元素进行遍历。 43 | 44 | 以`java.util.ArrayList`为例,它的`iterator()`方法返回的是`Iterator`的其内部类的实现: 45 | 46 | java.util.ArrayList.java 47 | 48 | public class ArrayList extends AbstractList 49 | implements List, RandomAccess, Cloneable, java.io.Serializable { 50 | 51 | // 实际存储元素的数据 52 | transient Object[] elementData; 53 | // 元素实际个数 54 | private int size; 55 | 56 | ... ... 57 | 58 | public Iterator iterator() { 59 | return new Itr(); 60 | } 61 | 62 | private class Itr implements Iterator { 63 | int cursor; // index of next element to return 64 | int lastRet = -1; // index of last element returned; -1 if no such 65 | ... ... 66 | 67 | public boolean hasNext() { 68 | return cursor != size; 69 | } 70 | 71 | @SuppressWarnings("unchecked") 72 | public E next() { 73 | ... ... 74 | int i = cursor; 75 | if (i >= size) 76 | throw new NoSuchElementException(); 77 | Object[] elementData = ArrayList.this.elementData; 78 | if (i >= elementData.length) 79 | throw new ConcurrentModificationException(); 80 | cursor = i + 1; 81 | return (E) elementData[lastRet = i]; 82 | } 83 | ... ... 84 | } //End of Itr 85 | ... ... 86 | } //End of ArrayList 87 | 88 | 其中去掉了一些代码。`ArrayList`是一种数组类型的`List`,其内部采用一个`Object[]`来保存所有元素,`size`用来保存一共有多少个元素。 89 | 90 | 方法`iterator()`会返回一个内部类`Itr`,后者实现了`Iterator`接口的`hasNext()`和`next()`方法。 91 | 92 | 既然是迭代遍历,那么就需要有一个变量能够记录遍历到哪个元素了,这里`Itr.cursor`就是用来记录迭代索引的变量。每次调用`hasNext()`判断后边是否还有元素的时候,其实就是比较这个索引的值是否和`size`相等;每次调用`next()`返回下一个元素,其实就是返回`elementData[cursor]`并让`cursor`自增以指向下一个元素。 93 | 94 | 这就是迭代器模式,如果去掉各种接口和类的继承关系,简单来说: 95 | 96 | ![迭代器模式](images/iterator-diagram.png) 97 | 98 | 迭代器模式是为集合类的事物服务的,因此类关系就很好说了:**一边是集合,一边是迭代器,集合能够返回迭代器,迭代器能够遍历集合。** 出于面向接口的更加灵活的模式设计,集合和迭代器均有抽象层(接口或抽象类)以及具体实现类。 99 | 100 | 最后,我们再回头看一下本文最初的例子: 101 | 102 | Iterator iterator = stringList.iterator(); 103 | while (iterator.hasNext()) { 104 | System.out.print(iterator.next() + " "); 105 | } 106 | 107 | 这四行就是迭代器模式的典型用法。`iterator`可能是一个`ArrayList`返回的,可能是一个`HashSet`返回的,我们都不care,只要获取到迭代器,我们就可以“无脑流”一路`hasNext() + next()`,这就是迭代器的初衷,它**为集合封装了迭代遍历元素的方法,留给用户的是一套简单易用的遍历接口。** 108 | -------------------------------------------------------------------------------- /iterator/images/Collection_interfaces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/iterator/images/Collection_interfaces.png -------------------------------------------------------------------------------- /iterator/images/iterator-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/iterator/images/iterator-diagram.png -------------------------------------------------------------------------------- /iterator/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.getset.designpatterns 8 | iterator 9 | 1.0-SNAPSHOT 10 | 11 | 12 | -------------------------------------------------------------------------------- /iterator/src/main/java/com/getset/designpatters/iterator/ArrayList.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatters.iterator; 2 | 3 | public class ArrayList implements Iterable { 4 | private transient Object[] elements; 5 | private int capacity = 16; 6 | private int size; 7 | 8 | public ArrayList(int capacity) { 9 | this.capacity = capacity; 10 | elements = new Object[capacity]; 11 | this.size = 0; 12 | } 13 | 14 | public boolean add(E obj) { 15 | if (size == capacity) { 16 | return false; 17 | } else { 18 | elements[size++] = obj; 19 | return true; 20 | } 21 | } 22 | 23 | public Iterator iterator() { 24 | return new Itr(); 25 | } 26 | 27 | private class Itr implements Iterator { 28 | 29 | private int cursor = 0; 30 | 31 | public boolean hasNext() { 32 | return cursor != size; 33 | } 34 | 35 | public E next() { 36 | return (E) elements[cursor++]; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /iterator/src/main/java/com/getset/designpatters/iterator/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatters.iterator; 2 | 3 | public class Client { 4 | public static void main(String[] args) { 5 | // Java SDK 6 | String[] strings = new String[]{"Hello,", "Java", "Design", "Patterns."}; 7 | java.util.List stringList = java.util.Arrays.asList(strings); 8 | java.util.Iterator iterator = stringList.iterator(); 9 | while (iterator.hasNext()) { 10 | System.out.print(iterator.next() + " "); 11 | } 12 | System.out.println(); 13 | // This example 14 | ArrayList list = new ArrayList(16); 15 | list.add("Hello,"); 16 | list.add("Java"); 17 | list.add("Design"); 18 | list.add("Patterns."); 19 | Iterator it = list.iterator(); 20 | while (it.hasNext()) { 21 | System.out.print(it.next() + " "); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /iterator/src/main/java/com/getset/designpatters/iterator/Iterable.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatters.iterator; 2 | 3 | public interface Iterable { 4 | Iterator iterator(); 5 | } 6 | -------------------------------------------------------------------------------- /iterator/src/main/java/com/getset/designpatters/iterator/Iterator.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatters.iterator; 2 | 3 | public interface Iterator { 4 | boolean hasNext(); 5 | T next(); 6 | } 7 | -------------------------------------------------------------------------------- /mediator/images/mediator-example1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/mediator/images/mediator-example1.png -------------------------------------------------------------------------------- /mediator/images/mediator-example2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/mediator/images/mediator-example2.png -------------------------------------------------------------------------------- /mediator/images/mediator-example3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/mediator/images/mediator-example3.png -------------------------------------------------------------------------------- /mediator/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | get-designpatterns 7 | com.getset.designpatterns 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | mediator 13 | 14 | 15 | -------------------------------------------------------------------------------- /mediator/src/main/java/com/getset/designpatterns/mediator/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.mediator; 2 | 3 | public class Client { 4 | public static void main(String[] args) { 5 | TeamMember xionger = new Developer("熊二"); 6 | TeamMember zhangsan = new Developer("张三"); 7 | TeamMember lisi = new Tester("李四"); 8 | TeamMember wangwu = new Operator("王五"); 9 | 10 | TechLeader leader = new TechLeader(); 11 | leader.addTeamMember(xionger); 12 | leader.addTeamMember(zhangsan); 13 | leader.addTeamMember(lisi); 14 | leader.addTeamMember(wangwu); 15 | 16 | zhangsan.reportToLeader("组长,世界很大,我想去看看,请假两天~"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /mediator/src/main/java/com/getset/designpatterns/mediator/Developer.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.mediator; 2 | 3 | public class Developer extends TeamMember { 4 | 5 | public Developer(String name) { 6 | super(name); 7 | this.role = TeamMember.RD; 8 | } 9 | 10 | public void dailyWork() { 11 | System.out.println("我是一个码农,我经常加班写代码,困了累了可能写出bug来。"); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /mediator/src/main/java/com/getset/designpatterns/mediator/Operator.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.mediator; 2 | 3 | public class Operator extends TeamMember { 4 | 5 | public Operator(String name) { 6 | super(name); 7 | this.role = TeamMember.OP; 8 | } 9 | 10 | public void dailyWork() { 11 | System.out.println("我是一个运维,保证系统稳定运行,如果有线上bug及时回滚,话说开发人员写的程序真不稳定。"); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /mediator/src/main/java/com/getset/designpatterns/mediator/TeamMember.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.mediator; 2 | 3 | public abstract class TeamMember { 4 | // 团队角色 5 | public static final String RD = "开发人员"; 6 | public static final String QA = "测试人员"; 7 | public static final String OP = "运维人员"; 8 | 9 | // 仅与自己的组长维护引用关系 10 | private TechLeader techLeader; 11 | private String name; 12 | protected String role; 13 | 14 | public TeamMember(String name) { 15 | this.name = name; 16 | } 17 | 18 | public String getName() { 19 | return name; 20 | } 21 | 22 | public void setTechLeader(TechLeader techLeader) { 23 | this.techLeader = techLeader; 24 | } 25 | 26 | // 向组长(调停者/中心角色)发送信息 27 | public void reportToLeader(String message) { 28 | techLeader.memberReport(this, message); 29 | } 30 | 31 | // 收到来自组长(调停者/中心角色)的消息 32 | public void tempTask(String task) { 33 | System.out.println("[" + role + "]" + name + "收到来自组长的安排: " + task); 34 | } 35 | 36 | // 无伤大雅的方法,与模式无关 37 | public abstract void dailyWork(); 38 | } 39 | -------------------------------------------------------------------------------- /mediator/src/main/java/com/getset/designpatterns/mediator/TechLeader.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.mediator; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class TechLeader { 7 | // 维护有各个组员的引用 8 | private List members; 9 | 10 | public TechLeader() { 11 | members = new ArrayList(); 12 | } 13 | 14 | public void addTeamMember(TeamMember teamMember) { 15 | members.add(teamMember); 16 | teamMember.setTechLeader(this); 17 | } 18 | 19 | public void memberReport(TeamMember reporter, String message) { 20 | if (message.contains("请假")) { 21 | reporter.tempTask("同意!"); 22 | // 对相关人员发送消息或安排其执行操作 23 | for (TeamMember m : members) { 24 | if (m.getName().equals(reporter.getName())) { 25 | continue; 26 | } else if (m.role.equals(TeamMember.RD)) { 27 | m.tempTask(reporter.getName() + "请假了,期间请接手他的开发工作。"); 28 | } else if (m.role.equals(TeamMember.QA)) { 29 | m.tempTask(reporter.getName() + "请假了,期间请将他的bug交由其他开发人员处理。"); 30 | } else if (m.role.equals(TeamMember.OP)) { 31 | m.tempTask(reporter.getName() + "请假了,期间请将他的线上问题交由其他开发人员处理。"); 32 | } 33 | } 34 | } else if (message.contains("建议")) { 35 | 36 | } else if (message.contains("技术分享")) { 37 | 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /mediator/src/main/java/com/getset/designpatterns/mediator/Tester.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.mediator; 2 | 3 | public class Tester extends TeamMember { 4 | 5 | public Tester(String name) { 6 | super(name); 7 | this.role = TeamMember.QA; 8 | } 9 | 10 | public void dailyWork() { 11 | System.out.println("我是一名测试,我找出bug,确保代码质量。"); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /memento/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | get-designpatterns 7 | com.getset.designpatterns 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | memento 13 | 14 | 15 | -------------------------------------------------------------------------------- /memento/src/main/java/com/getset/designpatterns/memento/Snapshot.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.memento; 2 | 3 | public interface Snapshot { 4 | } 5 | -------------------------------------------------------------------------------- /memento/src/main/java/com/getset/designpatterns/memento/User.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.memento; 2 | 3 | import java.util.Stack; 4 | 5 | public class User { 6 | public static void main(String[] args) { 7 | Stack snapshots = new Stack(); 8 | 9 | VirtualMachine ubuntu = new VirtualMachine("ubuntu", "1个4核CPU,8G内存,80G硬盘"); 10 | 11 | ubuntu.startup(); 12 | ubuntu.openApp("网易云音乐"); 13 | ubuntu.openApp("谷歌浏览器"); 14 | ubuntu.saveFile("/tmp/test.txt"); 15 | System.out.println(ubuntu); 16 | 17 | snapshots.push(ubuntu.takeSnapshot()); 18 | 19 | ubuntu.closeApp("网易云音乐"); 20 | ubuntu.openApp("IntelliJ IDEA"); 21 | ubuntu.delFile("/tmp/test.txt"); 22 | ubuntu.saveFile("/workspace/hello.java"); 23 | System.out.println(ubuntu); 24 | 25 | ubuntu.restoreSnapshot(snapshots.peek()); 26 | System.out.println("恢复到最近的快照..."); 27 | 28 | System.out.println(ubuntu); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /memento/src/main/java/com/getset/designpatterns/memento/VirtualMachine.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.memento; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class VirtualMachine { 7 | // 虚拟机名称 8 | private String name; 9 | // 虚拟机配置 10 | private String devices; 11 | // 虚拟机内存内容,简化为一个String的列表 12 | private List memory; 13 | // 虚拟机存储内容,简化为一个String的列表 14 | private List storage; 15 | // 虚拟机状态 16 | private String state; 17 | 18 | public VirtualMachine(String name, String devices) { 19 | this.name = name; 20 | this.devices = devices; 21 | this.memory = new ArrayList(); 22 | this.storage = new ArrayList(); 23 | this.state = "created"; 24 | } 25 | 26 | /** 27 | * 创建虚拟机 28 | * @param name 虚拟机名称 29 | * @param devices 虚拟机配置 30 | */ 31 | public VirtualMachine createVM(String name, String devices) { 32 | return new VirtualMachine(name, devices); 33 | } 34 | 35 | // 开机 36 | public void startup() { 37 | this.state = "running"; 38 | System.out.println("虚拟机" + name + "已启动"); 39 | } 40 | 41 | // 关机 42 | public void halt() { 43 | this.state = "shutdown"; 44 | System.out.println("虚拟机" + name + "已关机"); 45 | } 46 | 47 | // 暂停 48 | public void suspend() { 49 | this.state = "suspending"; 50 | System.out.println("虚拟机" + name + "已暂停"); 51 | } 52 | 53 | // 暂停后恢复 54 | public void resume() { 55 | this.state = "running"; 56 | System.out.println("虚拟机" + name + "已恢复"); 57 | } 58 | 59 | /** 60 | * 打开应用,加载到内存,用来模拟内存中的内容 61 | */ 62 | public void openApp(String appName) { 63 | if ("running".equals(state)) { 64 | this.memory.add(appName); 65 | System.out.println("虚拟机" + name + "打开应用: " + appName); 66 | } 67 | } 68 | 69 | /** 70 | * 关闭应用,从内存中删除,用来模拟内存中的内容 71 | */ 72 | public void closeApp(String appName) { 73 | if ("running".equals(state)) { 74 | this.memory.remove(appName); 75 | System.out.println("虚拟机" + name + "关闭应用: " + appName); 76 | } 77 | } 78 | 79 | /** 80 | * 保存文件,写入虚拟磁盘,用来模拟存储中的内容 81 | */ 82 | public void saveFile(String file) { 83 | if ("running".equals(state)) { 84 | this.storage.add(file); 85 | System.out.println("虚拟机" + name + "中保存文件: " + file); 86 | } 87 | } 88 | 89 | /** 90 | * 删除文件,从虚拟磁盘中删除,用来模拟存储中的内容 91 | */ 92 | public void delFile(String file) { 93 | if ("running".equals(state)) { 94 | this.storage.remove(file); 95 | System.out.println("虚拟机" + name + "中删除文件: " + file); 96 | } 97 | } 98 | 99 | /** 100 | * 打快照,如果是开机状态会保存内存快照和存储快照;如果是关机状态则仅保存存储快照即可。 101 | */ 102 | public Snapshot takeSnapshot() { 103 | if ("shutdown".equals(state)) { 104 | return new VMSnapshot(null, new ArrayList(storage)); 105 | } else { 106 | return new VMSnapshot(new ArrayList(memory), new ArrayList(storage)); 107 | } 108 | } 109 | 110 | /** 111 | * 恢复快照 112 | */ 113 | public void restoreSnapshot(Snapshot snapshot) { 114 | VMSnapshot tmp = (VMSnapshot)snapshot; 115 | this.memory = new ArrayList(tmp.memory); 116 | this.storage = new ArrayList(tmp.storage); 117 | if (tmp.memory == null) { 118 | this.state = "shutdown"; 119 | } 120 | } 121 | 122 | @Override 123 | public String toString() { 124 | StringBuffer stringBuffer = new StringBuffer(); 125 | stringBuffer.append("------\n[虚拟机“" + name + "”] 配置为“" + devices + "”," + "目前状态为:" + state + "。"); 126 | if ("running".equals(state)) { 127 | stringBuffer.append("\n 目前运行中的应用有:" + memory.toString()); 128 | stringBuffer.append("\n 最近保存的文件有:" + storage.toString()); 129 | } 130 | stringBuffer.append("\n------"); 131 | return stringBuffer.toString(); 132 | } 133 | 134 | private static class VMSnapshot implements Snapshot { 135 | private List memory; 136 | private List storage; 137 | 138 | public VMSnapshot(List memory, List storage) { 139 | this.memory = memory; 140 | this.storage = storage; 141 | } 142 | 143 | public List getMemory() { 144 | return memory; 145 | } 146 | 147 | public List getStorage() { 148 | return storage; 149 | } 150 | 151 | public void setMemory(List memory) { 152 | this.memory = memory; 153 | } 154 | 155 | public void setStorage(List storage) { 156 | this.storage = storage; 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /observer/README.md: -------------------------------------------------------------------------------- 1 | 观察者(Observer)模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,主体对象的状态变化会通知所有观察者对象。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。 2 | 3 | 这种模式在我们实际生活中并不鲜见,比如订牛奶、订报纸。我们订阅了某报纸之后,一旦报纸有新版出来,就会送到我们报箱或手中,去过取消订阅,那么也就再也收不到了。有了互联网之后,无论是微博好友还是微信订阅号,我们都可以“关注”和“取消关注”,关注了就可以收到信息推动。这些都是观察者模式的现实体现。 4 | 5 | # 例子 6 | 7 | 以微信订阅号的关注和消息推送为例。 8 | 9 | 无论是微信的订阅号、微博的大V、喜马拉雅的音频专辑,都可以被关注或取消关注,当有新的文章、消息、音频作品出现的时候,订阅了的粉丝都会收到消息。所以我们可以抽象出来一个共同的抽象类`Publisher`来实现这些公共的方法: 10 | 11 | Publisher.java 12 | 13 | public abstract class Publisher { 14 | private List funs = new ArrayList(); 15 | private String message; 16 | 17 | public void publishMessage(String message) { 18 | this.message = message; 19 | notifyAllSubscribers(); 20 | } 21 | 22 | public String getMessage() { 23 | return message; 24 | } 25 | 26 | public void addSubscriber(Subscriber subscriber) { 27 | funs.add(subscriber); 28 | } 29 | public void delSubscriber(Subscriber subscriber) { 30 | funs.remove(subscriber); 31 | } 32 | public void notifyAllSubscribers() { 33 | Iterator it = funs.iterator(); 34 | while (it.hasNext()) { 35 | it.next().update(this); 36 | } 37 | } 38 | } 39 | 40 | 可以看到,`Publisher`维护有一个订阅者的集合,当有新的内容更新时(其中`message`统一表示文章、信息或声音作品等各种形式的内容,由`publishMessage`更新内容),会调用`notifyAllSubscibers`方法来通知所有关注人。 41 | 这里要注意的是,这种通知是一种回调行为,也就是通过遍历并调用各个`subscriber`的`update`方法来进行通知。我们再来看一下`Subscriber`: 42 | 43 | Subscriber.java 44 | 45 | public interface Subscriber { 46 | void update(Publisher publisher); 47 | } 48 | 49 | 可见`Subscriber`只规定了一种方法,那就是被回调的`update`。 50 | 51 | 具体来说,微信订阅号就是一种`Publisher`: 52 | 53 | WeixinPublisher.java 54 | 55 | public class WeixinPublisher extends Publisher { 56 | private String dingyuehao; 57 | 58 | public WeixinPublisher(String dingyuehao) { 59 | this.dingyuehao = dingyuehao; 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return "微信订阅号[" + dingyuehao + "]"; 65 | } 66 | } 67 | 68 | 而普通的微信用户——即关注者——就是`Subscriber`(通过继承实现): 69 | 70 | WeixinAccount.java 71 | 72 | public class WeixinAccount implements Subscriber { 73 | private String accountName; 74 | 75 | public WeixinAccount(String accountName) { 76 | this.accountName = accountName; 77 | } 78 | 79 | public void update(Publisher publisher) { 80 | System.out.println(accountName + "的微信收到了来自" + publisher + "的推送文章: " + publisher.getMessage()); 81 | } 82 | } 83 | 84 | 微信用户实现了具体的`update`方法,定义了在收到通知后要做哪些操作,比如阅读、转发等等,这里通过打印一行文字来表示。 85 | 86 | 我们来看一下效果: 87 | 88 | Client.java 89 | 90 | public class Client { 91 | public static void main(String[] args) { 92 | WeixinPublisher publisher = new WeixinPublisher("享学IT"); 93 | publisher.addSubscriber(new WeixinAccount("张三")); 94 | publisher.addSubscriber(new WeixinAccount("李四")); 95 | publisher.addSubscriber(new WeixinAccount("王五")); 96 | 97 | publisher.publishMessage("Java设计模式百例-观察者模式"); 98 | } 99 | } 100 | 101 | 张三或李四执行“关注”操作后,微信订阅号执行`addSubscriber`操作将他们添加到自己的订阅者名单中,当发布新消息时,订阅者都可以收到,我们看一下输出: 102 | 103 | 张三的微信收到了来自微信订阅号[享学IT]的推送文章: Java设计模式百例-观察者模式 104 | 李四的微信收到了来自微信订阅号[享学IT]的推送文章: Java设计模式百例-观察者模式 105 | 王五的微信收到了来自微信订阅号[享学IT]的推送文章: Java设计模式百例-观察者模式 106 | 107 | # 总结 108 | 109 | 例子看完后,用一个类图“鸟瞰”一下类和接口关系就比较清晰了: 110 | 111 | ![观察者模式类关系图](images/observer.png) 112 | 113 | 抱歉,这个类关系图的布局不是很直观,但是有几个观察者模式的特点是可以总结出来的: 114 | 1. 观察者模式是一个一对多的关系,一个被观察者对应多个观察者,这种关系通过在被观察者内维护一个观察者的集合来实现。 115 | 2. 但是与“被围观”不同的是,被观察者拥有添加和删除观察者的方法,主动权在自己手中。 116 | 2. 当被观察者状态有变动时,也是由被观察者主动通知自己维护的“名单”中的各个观察者,通知是采用回调接口方法的方式。 117 | 118 | ## Java内置观察者模式 119 | 120 | 由于观察者模式应用广泛,Java内置了观察者模式的抽象类和接口: 121 | 122 | 被观察者 Observable.java 123 | 124 | ![Java-Observable](images/Java-Observable.png) 125 | 126 | 可以看到其中的关键方法`addObserver`、`deleteObserver`和`notifyObservers`。这样,上边的例子就不用自己写抽象类`Publisher`了,直接使用`Observable`即可。 127 | 128 | 观察者 Observer.java 接口也是同样的,不过`update`方法的参数更加具有普适性: 129 | 130 | public interface Observer { 131 | void update(Observable o, Object arg); 132 | } 133 | 134 | 其中,第二个参数`arg`是`Observable.notifyObservers(Object)`方法的参数传过来的内容。 135 | 136 | 使用模式与咱们的例子是一样的: 137 | 138 | ![Java观察者模式](images/Java-observer.png) 139 | 140 | 偷懒截了《Java与模式》的图。 141 | 142 | 大家在看源码的时候如果发现了类名类似`XxxObserver`或`XxxListener`这样的类时,不妨看一下,有可能就是应用了观察者模式。 -------------------------------------------------------------------------------- /observer/images/Java-Observable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/observer/images/Java-Observable.png -------------------------------------------------------------------------------- /observer/images/Java-observer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/observer/images/Java-observer.png -------------------------------------------------------------------------------- /observer/images/observer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/observer/images/observer.png -------------------------------------------------------------------------------- /observer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.getset.designpatterns 8 | observer 9 | 1.0-SNAPSHOT 10 | 11 | 12 | -------------------------------------------------------------------------------- /observer/src/main/java/com/getset/designpatterns/observer/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.observer; 2 | 3 | public class Client { 4 | public static void main(String[] args) { 5 | WeixinPublisher publisher = new WeixinPublisher("享学IT"); 6 | publisher.addSubscriber(new WeixinAccount("张三")); 7 | publisher.addSubscriber(new WeixinAccount("李四")); 8 | publisher.addSubscriber(new WeixinAccount("王五")); 9 | 10 | publisher.publishMessage("Java设计模式百例-观察者模式"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /observer/src/main/java/com/getset/designpatterns/observer/Publisher.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.observer; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Iterator; 5 | import java.util.List; 6 | 7 | public abstract class Publisher { 8 | private List funs = new ArrayList(); 9 | private String message; 10 | 11 | public void publishMessage(String message) { 12 | this.message = message; 13 | notifyAllSubscribers(); 14 | } 15 | 16 | public String getMessage() { 17 | return message; 18 | } 19 | 20 | public void addSubscriber(Subscriber subscriber) { 21 | funs.add(subscriber); 22 | } 23 | public void delSubscriber(Subscriber subscriber) { 24 | funs.remove(subscriber); 25 | } 26 | public void notifyAllSubscribers() { 27 | Iterator it = funs.iterator(); 28 | while (it.hasNext()) { 29 | it.next().update(this); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /observer/src/main/java/com/getset/designpatterns/observer/Subscriber.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.observer; 2 | 3 | public interface Subscriber { 4 | void update(Publisher publisher); 5 | } 6 | -------------------------------------------------------------------------------- /observer/src/main/java/com/getset/designpatterns/observer/WeixinAccount.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.observer; 2 | 3 | public class WeixinAccount implements Subscriber { 4 | private String accountName; 5 | 6 | public WeixinAccount(String accountName) { 7 | this.accountName = accountName; 8 | } 9 | 10 | public void update(Publisher publisher) { 11 | System.out.println(accountName + "的微信收到了来自" + publisher + "的推送文章: " + publisher.getMessage()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /observer/src/main/java/com/getset/designpatterns/observer/WeixinPublisher.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.observer; 2 | 3 | public class WeixinPublisher extends Publisher { 4 | private String dingyuehao; 5 | 6 | public WeixinPublisher(String dingyuehao) { 7 | this.dingyuehao = dingyuehao; 8 | } 9 | 10 | @Override 11 | public String toString() { 12 | return "微信订阅号[" + dingyuehao + "]"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.getset.designpatterns 8 | get-designpatterns 9 | 1.0-SNAPSHOT 10 | pom 11 | 12 | 13 | UTF-8 14 | 15 | 16 | 17 | simple-factory 18 | factory-method 19 | abstract-factory 20 | singleton 21 | builder 22 | prototype 23 | adaptor 24 | bridge 25 | proxy 26 | decorator 27 | composite 28 | observer 29 | facade 30 | strategy 31 | template-method 32 | command 33 | chainofresponsibility 34 | flyweight 35 | iterator 36 | interpreter 37 | mediator 38 | state 39 | memento 40 | visitor 41 | 42 | 43 | -------------------------------------------------------------------------------- /prototype/README.md: -------------------------------------------------------------------------------- 1 | > 本文源码见:https://github.com/get-set/get-designpatterns/tree/master/prototype 2 | 3 | 原型模式(Prototype Pattern)用于创建重复的对象,这种类型的设计模式属于创建型模式,与工厂模式类似,不同在于工厂模式通过`new`的方式创建对象,而原型模式通过复制既有对象的方式创建对象。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。 4 | 5 | # 例子 6 | 既然原型模式也是创建型模式,那么我们就用之前工厂模式的例子,也方便比较不同点。 7 | 8 | 我们在用画图软件或做PPT的时候经常用到的操作就是复制图形。为什么复制呢?因为现画一个图形比较麻烦嘛,需要调整大小、设置填充色、边框颜色、图形内字体大小颜色等等。这也是原型模式用到的一种场景,那就是当类属性多而且复杂的时候,在new的时候传入各种参数给构造方法,不如直接用一个现有的对象直接复制一个来的痛快。 9 | 10 | 假设我们在用PPT作图,无论是圆形、矩形还是三角形,都有填充颜色和边框颜色,以及图形内文字,因此可以抽象出一个`Shape`的抽象类,并且重写了`clone`方法(关于Java的`clone`,请参考“[番外篇 - Java的clone](ABOUT_CLONE.md)”)。 11 | 12 | Shape.java 13 | 14 | public abstract class Shape implements Cloneable { 15 | protected String type; 16 | protected String fillColor; 17 | protected String frameColor; 18 | protected String innerText; 19 | 20 | @Override 21 | public Shape clone() throws CloneNotSupportedException { 22 | return (Shape)super.clone(); 23 | } 24 | 25 | public abstract void draw(); 26 | 27 | // getters & setters ... ... 28 | 29 | } 30 | 31 | 各种不同的图形均继承`Shape`,同样也继承了其`clone()`方法。 32 | 33 | Circle.java 34 | 35 | public class Circle extends Shape { 36 | public Circle() { 37 | this.type = "circle"; 38 | } 39 | 40 | public void draw() { 41 | System.out.println("Draw a " + type + ", fill with " + fillColor + ", frame color is " + frameColor + ", inner text is [" + innerText + "]."); 42 | } 43 | } 44 | 45 | Rectangle.java 46 | 47 | public class Rectangle extends Shape { 48 | public Rectangle() { 49 | this.type = "rectangle"; 50 | } 51 | 52 | public void draw() { 53 | System.out.println("Draw a " + type + ", fill with " + fillColor + ", frame color is " + frameColor + ", inner text is [" + innerText + "]."); 54 | } 55 | } 56 | 57 | Triangle.java 58 | 59 | public class Triangle extends Shape { 60 | public Triangle() { 61 | this.type = "triangle"; 62 | } 63 | 64 | public void draw() { 65 | System.out.println("Draw a " + type + ", fill with " + fillColor + ", frame color is " + frameColor + ", inner text is [" + innerText + "]."); 66 | } 67 | } 68 | 69 | 下面我们来在main方法中使用一下这个原型复制的能力。 70 | 71 | Client.java 72 | 73 | public static void main(String[] args) throws CloneNotSupportedException { 74 | Shape circle1 = new Circle(); 75 | circle1.setFillColor("red"); 76 | circle1.setFrameColor("green"); 77 | circle1.setInnerText("Design patterns"); 78 | circle1.draw(); 79 | 80 | Shape circle2 = circle1.clone(); 81 | circle2.draw(); 82 | } 83 | 84 | 输出为: 85 | 86 | Draw a circle, fill with red, frame color is green, inner text is [Design patterns]. 87 | Draw a circle, fill with red, frame color is green, inner text is [Design patterns]. 88 | 89 | 这就是原型模式。 90 | 91 | # 总结 92 | 通过上述内容,你大概也知道了原型模型的应用场景,那就是“当new一个对象成本较高的时候”,再举几个例子: 93 | 94 | 1. 当一个系统应该独立于它的产品创建,构成和表示时。 95 | 2. 当要实例化的类是在运行时指定时,例如,通过动态装载。 96 | 3. 为了避免创建一个与产品类层次平行的工厂类层次时。 97 | 4. 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。 98 | 99 | 除此之外,还有别的好处哟: 100 | 101 | 1. 性能好。在[番外篇 - Java的clone](ABOUT_CLONE.md)中说到,`clone()`是源自`Object`类中的`native`方法,其执行效率相对较高。 102 | 2. 可以逃避构造方法的束缚,因为不需要`new`了嘛。 103 | 104 | 使用原型模式有几点注意事项: 105 | 106 | 1. 类本身或其父类必须实现`Cloneable`接口,并用`public`的`clone()`方法覆盖`Object`的同名方法; 107 | 2. 根据实际情况判断是”浅复制“还是”深复制“,若深复制用序列化方式实现,则类本身或其父类须实现`Serializable`接口; 108 | 3. 实际开发过程中注意业务逻辑,比如id等成员要保证不重复。 109 | 110 | 其实结合最近几个设计模式的例子,你可能会发现,我前边介绍到的设计模式的类关系模型,有些是同GoF的一致的,有些是有差别的。其实看网上其他的设计模式的博文介绍也有这种感觉,那就是似乎感觉同样一个设计模式,不同的文章介绍出来类关系怎么不一样。比如对于抽象,有的是用接口,有的是用抽象类;比如对于继承关系,有的是一层,有的是两层。这时候总有些不知所措:到底哪个是正宗的呢? 111 | 112 | 其实对于设计模式,大可不必追求“正宗”,就像到了长沙,各种臭豆腐店,尝遍之后也很难说哪个是正宗的,但是它们都有共同点:基本是黑色的黑暗料理,闻起来有点臭但是吃起来香,烹饪方法都是油炸等等。抓住这些基本特点,只要好吃,那就OK咯~说回设计模式,归根到底是对“面向对象思想”应用的一些成熟的模型,不要“拿来主义”生搬硬套,抓住核心特征,符合实际情况即可,所谓“无招胜有招”嘛。 113 | 114 | 那么对于原型模式,其核心特征在于`clone`。 -------------------------------------------------------------------------------- /prototype/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | prototype 7 | 8 | 9 | com.getset.designpatterns 10 | get-designpatterns 11 | 1.0-SNAPSHOT 12 | ../ 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /prototype/src/main/java/com/getset/designpatterns/clonetest/ClassA.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.clonetest; 2 | 3 | public class ClassA implements Cloneable { 4 | public int getA() { 5 | return a; 6 | } 7 | 8 | public void setA(int a) { 9 | this.a = a; 10 | } 11 | 12 | private int a; 13 | 14 | @Override 15 | public ClassA clone() throws CloneNotSupportedException { 16 | return (ClassA) super.clone(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /prototype/src/main/java/com/getset/designpatterns/clonetest/ClassB.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.clonetest; 2 | 3 | public class ClassB extends ClassA { 4 | private String b; 5 | 6 | public String getB() { 7 | return b; 8 | } 9 | 10 | public void setB(String b) { 11 | this.b = b; 12 | } 13 | 14 | public void test() { 15 | System.out.println(super.getClass().getCanonicalName()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /prototype/src/main/java/com/getset/designpatterns/clonetest/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.clonetest; 2 | 3 | import java.io.IOException; 4 | 5 | public class Client { 6 | public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException { 7 | 8 | // 克隆羊多利例子的测试 9 | Sheep jane = new Sheep("简", 5, "多塞特白面绵羊", new EarTag(12345, "黄色")); 10 | System.out.println(jane); 11 | // Sheep dolly = jane.clone(); // 浅复制 12 | // Sheep dolly = jane.deepClone(); // 深复制 13 | Sheep dolly = jane.serializedClone(); // 基于序列化进行深复制 14 | System.out.println("克隆后..."); 15 | System.out.println(jane.equals(dolly)); 16 | dolly.setName("多利"); 17 | dolly.setAge(6); 18 | dolly.getEarTag().setId(12346); 19 | System.out.println(dolly); 20 | System.out.println(jane); 21 | System.out.println(jane.getEarTag() == dolly.getEarTag()); 22 | System.out.println(jane.equals(dolly)); 23 | 24 | // 继承关系clone的例子 25 | ClassB b = new ClassB(); 26 | b.setA(1); 27 | b.setB("b"); 28 | ClassB b1 = (ClassB) b.clone(); 29 | System.out.println(b1.getB()); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /prototype/src/main/java/com/getset/designpatterns/clonetest/EarTag.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.clonetest; 2 | 3 | import java.io.Serializable; 4 | 5 | public class EarTag implements Cloneable, Serializable { 6 | private int id; 7 | private String color; 8 | 9 | public EarTag(int id, String color) { 10 | this.id = id; 11 | this.color = color; 12 | } 13 | 14 | public int getId() { 15 | return id; 16 | } 17 | 18 | public void setId(int id) { 19 | this.id = id; 20 | } 21 | 22 | public String getColor() { 23 | return color; 24 | } 25 | 26 | public void setColor(String color) { 27 | this.color = color; 28 | } 29 | 30 | @Override 31 | public EarTag clone() throws CloneNotSupportedException { 32 | return (EarTag) super.clone(); 33 | } 34 | 35 | @Override 36 | public boolean equals(Object obj) { 37 | if (obj == this) 38 | return true; 39 | if (!(obj instanceof EarTag)) 40 | return false; 41 | EarTag earTag = (EarTag) obj; 42 | return earTag.id == this.id && earTag.color.equals(this.color); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /prototype/src/main/java/com/getset/designpatterns/clonetest/Sheep.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.clonetest; 2 | 3 | import java.io.*; 4 | 5 | public class Sheep implements Cloneable, Serializable { 6 | private String name; 7 | private int age; 8 | private String breed; 9 | private EarTag earTag; 10 | 11 | public Sheep(String name, int age, String breed, EarTag earTag) { 12 | this.name = name; 13 | this.age = age; 14 | this.breed = breed; 15 | this.earTag = earTag; 16 | } 17 | 18 | public String getName() { 19 | return name; 20 | } 21 | 22 | public void setName(String name) { 23 | this.name = name; 24 | } 25 | 26 | public int getAge() { 27 | return age; 28 | } 29 | 30 | public void setAge(int age) { 31 | this.age = age; 32 | } 33 | 34 | public String getBreed() { 35 | return breed; 36 | } 37 | 38 | public void setBreed(String breed) { 39 | this.breed = breed; 40 | } 41 | 42 | public EarTag getEarTag() { 43 | return earTag; 44 | } 45 | 46 | public void setEarTag(EarTag earTag) { 47 | this.earTag = earTag; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return this.name + "是一只" + this.age + "岁的" + this.breed + ", 它的" + this.earTag.getColor() + "色耳牌上写着" + this.earTag.getId() + "号。"; 53 | } 54 | 55 | @Override 56 | public Sheep clone() throws CloneNotSupportedException { 57 | return (Sheep) super.clone(); 58 | } 59 | 60 | public Sheep deepClone() throws CloneNotSupportedException { 61 | Sheep s = (Sheep)super.clone(); 62 | s.setEarTag(s.getEarTag().clone()); 63 | return s; 64 | } 65 | 66 | public Sheep serializedClone() throws IOException, ClassNotFoundException { 67 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 68 | ObjectOutputStream oo = new ObjectOutputStream(bao); 69 | oo.writeObject(this); 70 | ByteArrayInputStream bai = new ByteArrayInputStream(bao.toByteArray()); 71 | ObjectInputStream oi = new ObjectInputStream(bai); 72 | return (Sheep) oi.readObject(); 73 | } 74 | 75 | @Override 76 | public boolean equals(Object obj) { 77 | if (obj == this) 78 | return true; 79 | if (!(obj instanceof Sheep)) 80 | return false; 81 | Sheep s = (Sheep) obj; 82 | return s.name.equals(this.name) && 83 | s.age == this.age && 84 | s.breed.equals(this.breed) && 85 | s.earTag.equals(this.earTag); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /prototype/src/main/java/com/getset/designpatterns/shape/Circle.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.shape; 2 | 3 | public class Circle extends Shape { 4 | public Circle() { 5 | this.type = "circle"; 6 | } 7 | 8 | public void draw() { 9 | System.out.println("Draw a " + type + ", fill with " + fillColor + ", frame color is " + frameColor + ", inner text is [" + innerText + "]."); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /prototype/src/main/java/com/getset/designpatterns/shape/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.shape; 2 | 3 | public class Client { 4 | 5 | public static void main(String[] args) throws CloneNotSupportedException { 6 | Shape circle1 = new Circle(); 7 | circle1.setFillColor("red"); 8 | circle1.setFrameColor("green"); 9 | circle1.setInnerText("Design patterns"); 10 | circle1.draw(); 11 | 12 | Shape circle2 = circle1.clone(); 13 | circle2.draw(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /prototype/src/main/java/com/getset/designpatterns/shape/Rectangle.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.shape; 2 | 3 | public class Rectangle extends Shape { 4 | public Rectangle() { 5 | this.type = "rectangle"; 6 | } 7 | 8 | public void draw() { 9 | System.out.println("Draw a " + type + ", fill with " + fillColor + ", frame color is " + frameColor + ", inner text is [" + innerText + "]."); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /prototype/src/main/java/com/getset/designpatterns/shape/Shape.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.shape; 2 | 3 | public abstract class Shape implements Cloneable { 4 | protected String type; 5 | protected String fillColor; 6 | protected String frameColor; 7 | protected String innerText; 8 | 9 | @Override 10 | public Shape clone() throws CloneNotSupportedException { 11 | return (Shape)super.clone(); 12 | } 13 | 14 | public abstract void draw(); 15 | 16 | public String getType() { 17 | return type; 18 | } 19 | 20 | public void setType(String type) { 21 | this.type = type; 22 | } 23 | 24 | public String getFillColor() { 25 | return fillColor; 26 | } 27 | 28 | public void setFillColor(String fillColor) { 29 | this.fillColor = fillColor; 30 | } 31 | 32 | public String getFrameColor() { 33 | return frameColor; 34 | } 35 | 36 | public void setFrameColor(String frameColor) { 37 | this.frameColor = frameColor; 38 | } 39 | 40 | public String getInnerText() { 41 | return innerText; 42 | } 43 | 44 | public void setInnerText(String innerText) { 45 | this.innerText = innerText; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /prototype/src/main/java/com/getset/designpatterns/shape/Triangle.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.shape; 2 | 3 | public class Triangle extends Shape { 4 | public Triangle() { 5 | this.type = "triangle"; 6 | } 7 | 8 | public void draw() { 9 | System.out.println("Draw a " + type + ", fill with " + fillColor + ", frame color is " + frameColor + ", inner text is [" + innerText + "]."); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /proxy/images/adaptor-pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/proxy/images/adaptor-pattern.png -------------------------------------------------------------------------------- /proxy/images/decorator-pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/proxy/images/decorator-pattern.png -------------------------------------------------------------------------------- /proxy/images/http-proxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/proxy/images/http-proxy.png -------------------------------------------------------------------------------- /proxy/images/http_proxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/proxy/images/http_proxy.png -------------------------------------------------------------------------------- /proxy/images/proxy-pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/proxy/images/proxy-pattern.png -------------------------------------------------------------------------------- /proxy/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | proxy 7 | 8 | 9 | com.getset.designpatterns 10 | get-designpatterns 11 | 1.0-SNAPSHOT 12 | ../ 13 | 14 | 15 | -------------------------------------------------------------------------------- /proxy/src/main/java/com/getset/designpatterns/proxy/httpproxy/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.proxy.httpproxy; 2 | 3 | import java.lang.reflect.Proxy; 4 | 5 | public class Client { 6 | public static void main(String[] args) { 7 | HttpServer overseasHttpServer = new OverseasHttpServer(); 8 | 9 | HttpServer httpServer = new HttpProxy(overseasHttpServer); 10 | httpServer.handleRequest(); 11 | 12 | HttpProxyHandler proxy = new HttpProxyHandler(overseasHttpServer); 13 | HttpServer httpProxy = (HttpServer) Proxy.newProxyInstance(proxy.getClass().getClassLoader(), overseasHttpServer.getClass().getInterfaces(), proxy); 14 | httpProxy.handleRequest(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /proxy/src/main/java/com/getset/designpatterns/proxy/httpproxy/HttpProxy.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.proxy.httpproxy; 2 | 3 | public class HttpProxy implements HttpServer { 4 | private HttpServer overseasHttpServer; 5 | 6 | public HttpProxy(HttpServer overseasHttpServer) { 7 | this.overseasHttpServer = overseasHttpServer; 8 | } 9 | 10 | public void handleRequest() { 11 | preRequest(); 12 | this.overseasHttpServer.handleRequest(); 13 | postRequest(); 14 | } 15 | 16 | public void preRequest() { 17 | System.out.println("拦截客户端的HTTP请求,发送至海外的代理服务器,由其向海外HTTP服务器发送请求..."); 18 | } 19 | public void postRequest() { 20 | System.out.println("海外代理服务其接收海外HTTP服务器反馈的结果,发送回客户端浏览器进行显示..."); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /proxy/src/main/java/com/getset/designpatterns/proxy/httpproxy/HttpProxyHandler.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.proxy.httpproxy; 2 | 3 | import java.lang.reflect.InvocationHandler; 4 | import java.lang.reflect.Method; 5 | 6 | public class HttpProxyHandler implements InvocationHandler { 7 | private Object obj; 8 | 9 | public HttpProxyHandler(Object obj) { 10 | this.obj = obj; 11 | } 12 | 13 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 14 | preRequest(); 15 | System.out.println(proxy.getClass().getName()); 16 | Object o = method.invoke(obj, args); 17 | postRequest(); 18 | return o; 19 | } 20 | 21 | public void preRequest() { 22 | System.out.println("拦截客户端的HTTP请求,发送至海外的代理服务器,由其向海外HTTP服务器发送请求..."); 23 | } 24 | public void postRequest() { 25 | System.out.println("海外代理服务其接收海外HTTP服务器反馈的结果,发送回客户端浏览器进行显示..."); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /proxy/src/main/java/com/getset/designpatterns/proxy/httpproxy/HttpServer.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.proxy.httpproxy; 2 | 3 | public interface HttpServer { 4 | void handleRequest(); 5 | } 6 | -------------------------------------------------------------------------------- /proxy/src/main/java/com/getset/designpatterns/proxy/httpproxy/OverseasHttpServer.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.proxy.httpproxy; 2 | 3 | public class OverseasHttpServer implements HttpServer { 4 | public void handleRequest() { 5 | System.out.println("海外的HTTP服务器处理请求并返回结果..."); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /proxy/src/main/java/com/getset/designpatterns/proxy/illustration/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.proxy.illustration; 2 | 3 | public class Client { 4 | public static void main(String[] args) { 5 | Subject proxySubject = new ProxySubject(new RealSubject()); 6 | proxySubject.request(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /proxy/src/main/java/com/getset/designpatterns/proxy/illustration/ProxySubject.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.proxy.illustration; 2 | 3 | public class ProxySubject implements Subject { 4 | private Subject realSubject; 5 | public ProxySubject(Subject realSubject) { 6 | this.realSubject = realSubject; 7 | } 8 | public void request() { 9 | preRequest(); 10 | this.realSubject.request(); 11 | postRequest(); 12 | } 13 | public void preRequest() { 14 | System.out.println("请求前..."); 15 | } 16 | public void postRequest() { 17 | System.out.println("请求后..."); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /proxy/src/main/java/com/getset/designpatterns/proxy/illustration/RealSubject.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.proxy.illustration; 2 | 3 | public class RealSubject implements Subject { 4 | public void request() { 5 | System.out.println("处理请求..."); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /proxy/src/main/java/com/getset/designpatterns/proxy/illustration/Subject.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.proxy.illustration; 2 | 3 | public interface Subject { 4 | void request(); 5 | } 6 | -------------------------------------------------------------------------------- /simple-factory/README.md: -------------------------------------------------------------------------------- 1 | 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象很好的方式。具体来说,有简单工厂模式(simple factory)、工厂方法模式(factory method)和抽象工厂模式(abstract factory)等模式。 2 | 本篇先从简单工厂模式谈起。 3 | 4 | # 例子 5 | 请设想一个简单的需求,你在做一个画图软件,可以画矩形、三角形和圆形等,每一种图形都用一个类来管理: 6 | * `Rectangle` 7 | * `Circle` 8 | * `Triangle` 9 | 每个类都有各自的`draw()`方法,当需要画某种图形的时候就可以: 10 | 11 | Client.java 12 | 13 | // 画矩形时: 14 | Rectangle r = new Rectangle(); 15 | r.draw(); 16 | // 画圆形时: 17 | Circle c = new Circle(); 18 | c.draw(); 19 | // 画三角形时: 20 | Triangle t = new Triangle(); 21 | t.draw(); 22 | 23 | 如果这时增加一个`Star`类型的图形,就得需要增加一个`new Star()`来获取对象,这显然是不符合开闭原则。所谓开闭原则,即对扩展开放,对修改关闭。 24 | 25 | 要想实现开闭原则,就需要考虑把创建对象的过程统一起来,并且能够满足扩展图形类型的需要。我们自然而然地就能想到这些图形都是有共同点可以抽象的,它们都实现了`draw()`方法,因此可以增加一个共同的接口,即“针对接口编程,依赖于抽象而不依赖于具体”,这就是“依赖倒转”的原则。 26 | 27 | Shape.java 28 | 29 | public interface Shape { 30 | void draw(); 31 | } 32 | 33 | 34 | Rectangle.java 35 | 36 | public class Rectangle implements Shape { 37 | @Override 38 | public void draw() { 39 | System.out.println("Draw a rectangle."); 40 | } 41 | } 42 | 43 | 44 | Triangle.java 45 | 46 | public class Triangle implements Shape { 47 | @Override 48 | public void draw() { 49 | System.out.println("Draw a triangle."); 50 | } 51 | } 52 | 53 | 54 | Circle.java 55 | 56 | public class Circle implements Shape { 57 | @Override 58 | public void draw() { 59 | System.out.println("Draw a circle."); 60 | } 61 | } 62 | 63 | 64 | 然后屏蔽掉具体类型的创建语句,那么就可以一定程度上做到“开闭原则”了。这时就需要工厂类登场了,它有一个用来返回具体产品对象的方法,注意这个方法是静态(static)的,也因此简单工厂模式有时候也被叫做“静态工厂模式”。 65 | 66 | ShapeFactory.java 67 | 68 | public class ShapeFactory { 69 | //使用 getShape 方法获取形状类型的对象 70 | public static Shape getShape(String shapeType){ 71 | if(shapeType == null){ 72 | return null; 73 | } 74 | if(shapeType.equalsIgnoreCase("CIRCLE")){ 75 | return new Circle(); 76 | } else if(shapeType.equalsIgnoreCase("RECTANGLE")){ 77 | return new Rectangle(); 78 | } else if(shapeType.equalsIgnoreCase("TRIANGLE")){ 79 | return new Triangle(); 80 | } 81 | return null; 82 | } 83 | } 84 | 85 | 86 | 有了工厂类,再画图的时候刚才的`Client.java`就可以改一改了: 87 | 88 | Client.java 89 | 90 | //画矩形时: 91 | Shape r = ShapeFactory.getShape("RECTANGLE"); 92 | r.draw(); 93 | //画圆形时: 94 | Shape c = ShapeFactory.getShape("CIRCLE"); 95 | c.draw(); 96 | //画三角形时: 97 | Shape t = ShapeFactory.getShape("TRIANGLE"); 98 | t.draw(); 99 | 100 | 此时如果增加一个Star的图形类型,也可以从容应对了,从使用者`Client`的角度,无需关心对象是如何创建的。 101 | 各个类之间的关系如下: 102 | 103 | ![](images/simplefactory1.png) 104 | 105 | 此外还有一个类似的例子,可自行查看源码: 106 | 107 | ![](images/simplefactory2.png) 108 | 109 | 看到这里可能你会觉得,前后两个版本的`Client`并没有太大的不同,反而采用了工厂模式后更加复杂了。其实作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是**复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。** 110 | 111 | 此外,在具体使用时,也会有不同的调整,比如: 112 | 1. `ShapeFactory`的`getShape(String)`方法也可以改为非`static`的,这样就需要首先构造一个实际的工厂对象来创建产品了; 113 | 2. 工厂类、抽象产品和具体产品这三个角色有可能部分或全部合并,我们来看一个Java中经常用到的关于日期类型格式化的例子: 114 | 115 | DateTest.java 116 | 117 | import java.util.*; 118 | import java.text.*; 119 | public class DateTest 120 | { 121 | public static void main(String[] args) 122 | { 123 | Locale local = Locale.FRENCH; 124 | Date date = new Date(); 125 | String now = DateFormat.getTimeInstance(DateFormat.DEFAULT,local).format(date); 126 | System.out.println(now); 127 | try 128 | { 129 | date = DateFormat.getDateInstance(DateFormat.DEFAULT,local).parse("16 nov. 01"); 130 | System.out.println(date); 131 | } 132 | catch (ParseException e) 133 | { 134 | System.out.println("Parsing exception:" + e); 135 | } 136 | } 137 | } 138 | 139 | 而我们知道DateFormat是个抽象类,`public final static DateFormat getDateInstance(...)`的静态方法,也使得它自身既是一个抽象产品角色,又是一个工厂类角色,完成具体产品角色的创建(比如`SimpleDateFormat`)。 140 | 141 | 如果三个角色全部合并,其实就是一个类,增加了一个创建自身对象的方法。 142 | 143 | # 总结 144 | 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。 145 | 146 | **使用场景**: 1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。 147 | 148 | **优点**: 一个调用者想创建一个对象,只要提出要求就可以了;屏蔽产品的具体实现,调用者只关心产品的接口;一定程度上提高了扩展性。 149 | 150 | **缺点**:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。可见,简单工厂模式对"开-闭"原则的支持还不够,因为如果有新的产品加入到系统中去,就需要修改工厂类,将必要的逻辑加入到工厂类中。 151 | 152 | -------------------------------------------------------------------------------- /simple-factory/images/simplefactory1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/simple-factory/images/simplefactory1.png -------------------------------------------------------------------------------- /simple-factory/images/simplefactory2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/simple-factory/images/simplefactory2.png -------------------------------------------------------------------------------- /simple-factory/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 4.0.0 7 | simple-factory 8 | 9 | 10 | com.getset.designpatterns 11 | get-designpatterns 12 | 1.0-SNAPSHOT 13 | ../ 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /simple-factory/src/main/java/com/getset/designpatterns/simplefactory1/Circle.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.simplefactory1; 2 | 3 | public class Circle implements Shape { 4 | public void draw() { 5 | System.out.println("Draw a circle."); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /simple-factory/src/main/java/com/getset/designpatterns/simplefactory1/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.simplefactory1; 2 | 3 | public class Client { 4 | public static void main(String[] args) { 5 | Shape c = ShapeFactory.CreateShape("CIRCLE"); 6 | c.draw(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /simple-factory/src/main/java/com/getset/designpatterns/simplefactory1/Rectangle.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.simplefactory1; 2 | 3 | public class Rectangle implements Shape { 4 | public void draw() { 5 | System.out.println("Draw a ectangle."); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /simple-factory/src/main/java/com/getset/designpatterns/simplefactory1/Shape.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.simplefactory1; 2 | 3 | public interface Shape { 4 | void draw(); 5 | } 6 | -------------------------------------------------------------------------------- /simple-factory/src/main/java/com/getset/designpatterns/simplefactory1/ShapeFactory.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.simplefactory1; 2 | 3 | public class ShapeFactory { 4 | public final static Shape CreateShape(String shape) { 5 | if (null == shape) { 6 | return null; 7 | } 8 | 9 | if (shape.equals("CIRCLE")) { 10 | return new Circle(); 11 | } else if (shape.equals("RECTANGLE")) { 12 | return new Rectangle(); 13 | } else if (shape.equals("TRIANGLE")) { 14 | return new Triangle(); 15 | } 16 | 17 | return null; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /simple-factory/src/main/java/com/getset/designpatterns/simplefactory1/Triangle.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.simplefactory1; 2 | 3 | public class Triangle implements Shape{ 4 | public void draw() { 5 | System.out.println("Draw a triangle."); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /simple-factory/src/main/java/com/getset/designpatterns/simplefactory2/American.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.simplefactory2; 2 | 3 | public class American implements Person { 4 | 5 | public String sayGoodbye(String name) { 6 | return "Hello," + name; 7 | } 8 | 9 | public String sayHello(String name) { 10 | return "Goodbye," + name; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /simple-factory/src/main/java/com/getset/designpatterns/simplefactory2/Chinese.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.simplefactory2; 2 | 3 | public class Chinese implements Person { 4 | 5 | public String sayHello(String name) { 6 | return "你好," + name; 7 | } 8 | 9 | public String sayGoodbye(String name) { 10 | return "再见," + name; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /simple-factory/src/main/java/com/getset/designpatterns/simplefactory2/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.simplefactory2; 2 | 3 | public class Client { 4 | 5 | /** 6 | * @param args 7 | */ 8 | public static void main(String[] args) { 9 | 10 | PersonFactory factory = new PersonFactory(); 11 | Person chinese = factory.getPerson("Chinese"); 12 | System.out.println(chinese.sayHello("张三")); 13 | System.out.println(chinese.sayGoodbye("张三")); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /simple-factory/src/main/java/com/getset/designpatterns/simplefactory2/Person.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.simplefactory2; 2 | 3 | public interface Person { 4 | 5 | public String sayHello(String name); 6 | public String sayGoodbye(String name); 7 | } 8 | -------------------------------------------------------------------------------- /simple-factory/src/main/java/com/getset/designpatterns/simplefactory2/PersonFactory.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.simplefactory2; 2 | 3 | public class PersonFactory { 4 | 5 | public Person getPerson(String name) { 6 | if("Chinese".equals(name)){ 7 | return new Chinese(); 8 | }else if("American".equals(name)){ 9 | return new American(); 10 | }else{ 11 | return null; 12 | } 13 | 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /singleton/README.md: -------------------------------------------------------------------------------- 1 | 2 | 单例模式(Singleton Pattern)是Java中最简单的设计模式之一,但也是一个很值得玩味儿的设计模式,这是一个创建型的模式。 3 | 4 | 单例模式的目的在于,对于一些类,需要保证其仅有一个实例。比如一个Web中的计数器,不用每次刷新在数据库里记录一次,可以用一个单例的对象先缓存起来。但是这个计数器不能有多个实例,否则岂不是不准了。 5 | 6 | 具体来说,该设计模式有如下特点或要求: 7 | 8 | 1. 单例类只能有一个实例。 9 | 2. 单例类必须自己创建自己的唯一实例。 10 | 3. 单例类必须给所有其他对象提供这一实例。 11 | 12 | # 例子 13 | ## 饿汉式 14 | 15 | 我们考虑一下上述的三个要求。 16 | 17 | 首先,单例类只能有一个实例。我们知道,一个类的`static`的成员变量或方法可以看作归属于类本身的,可以被这个类所有的实例共享,可以认为是”独一份儿“的。所以用`static`关键字修饰这个类的对象,就可以做到。 18 | 19 | 然后,单例类必须自己创建自己的唯一实例。那也好办,单例类自己定义一个类型为自己的成员变量,然后设置为`static`的就可以了。然后把构造方法设置为`private`的,这样就不能被其他类或对象`new`了。 20 | 21 | 最后,单例类必须给所有其他对象提供这一实例。那就是提供一个get方法可以获取到这个类型为自己的成员变量。 22 | 23 | 分析过后,先撸一版代码: 24 | 25 | SingleObject.java 26 | 27 | public class SingleObject { 28 | 29 | private final static SingleObject INSTANCE = new SingleObject(); 30 | 31 | private SingleObject() {} 32 | 33 | public static SingleObject getInstance() { 34 | return instance; 35 | } 36 | } 37 | 38 | 我们搞一个Client检验一下: 39 | 40 | Client.java 41 | 42 | public class Client { 43 | public static void main(String[] args) { 44 | System.out.println(SingleObject.getInstance()); 45 | System.out.println(SingleObject.getInstance()); 46 | } 47 | } 48 | 49 | 返回结果: 50 | 51 | com.getset.designpatterns.singleton.SingleObject@5e2de80c 52 | com.getset.designpatterns.singleton.SingleObject@5e2de80c 53 | 54 | 根据对象的hash可以看出,两次调用`getInstance()`返回的是同一个对象。 55 | 56 | 这种是比较常用的方式,叫做“饿汉式”,为啥叫这名自己体会哟~ 我们知道,类加载时,static的成员变量会初始化,所以一旦类加载,那么`INSTANCE`所指向的对象就被创建了。其实这就是个常亮了,所以可以用大写,然后`final`修饰。 57 | 58 | ## 懒汉式 59 | 60 | 在有些情况下,如果单例类本身比较消耗资源或加载缓慢,希望能够在使用的时候才创建实例,那么可以采用懒加载的方式,如下: 61 | 62 | LazySingleObject.java 63 | 64 | // 本例是线程不安全的 65 | public class LazySingleObject { 66 | private static LazySingleObject instance; 67 | private LazySingleObject() {} 68 | 69 | public static LazySingleObject getInstance() { 70 | if (instance == null) { 71 | instance = new LazySingleObject(); 72 | } 73 | return instance; 74 | } 75 | } 76 | 77 | 如此,只有在调用`getInstance()`的时候才会创建实例。 78 | 79 | 但是这种方式是线程不安全的,假设有两个线程同时调用了`getInstance()`,可能都会检测到`instance == null`。所以可以把`getInstance()`方法使用`synchronized`修饰,以便保证线程安全。 80 | 81 | 但是在实际使用过程中,一旦实例被创建后,`getInstance()`方法只是返回实例,是不需要同步的,而加锁会影响效率,因此我们考虑不对整个方法加锁,而仅仅只对`new`实例的过程加锁,如下: 82 | 83 | LazySingleObject.java 84 | 85 | // 双检锁/双重校验锁(DCL,即 double-checked locking) 86 | public class LazySingleObject { 87 | // 使用volatile修饰单例变量 88 | private static volatile LazySingleObject instance; 89 | private LazySingleObject() {} 90 | public static LazySingleObject getInstance() { 91 | // 第一次判断,此时是未加锁的 92 | if (instance == null) { 93 | synchronized (LazySingleObject.class) { 94 | // 第二次判断,此时是线程安全的 95 | if (instance == null) { 96 | instance = new LazySingleObject(); 97 | } 98 | } 99 | } 100 | return instance; 101 | } 102 | } 103 | 104 | 可见,只有在实例未创建(`instance == null`)的时候才同步创建实例对象。在`synchronized`代码库内部,再次检查实例是否创建,因为第一次检查并不是线程安全的。也因此,这种方式叫做“双检锁/双重校验锁”。这样,一旦实例创建之后,就不再进入同步代码块了,从而效率更高。 105 | 106 | 要特别注意的是,单例变量`instance`要用`volatile`进行修饰。原因在于编译器出于优化需要会对内存的读写操作重排序,因此LazySingleObject对象初始化时的写操作与写入instance字段的操作可以是无序的。导致的结果就是如果某个线程调用getInstance()可能看到instance字段指向了一个LazySingleObject对象,但看到该对象里的字段值却是默认值,而不是在LazySingleObject构造方法里设置的那些值。而使用`volatile`字段修饰后,编译器和运行时都会注意到这是个共享变量,因此不会将该变量上的操作和其他内存操作一起重排序,真正保证线程安全。 107 | 108 | 以上两种方式可根据场景选择。 109 | 110 | ## 登记式/静态内部类 111 | 112 | 如果感觉“双检锁/双重校验锁”这种方式复杂难学,可以采用静态内部类来实现线程安全的懒加载。 113 | 114 | LazyHandlerSingleObject.java 115 | 116 | public class LazyHandlerSingleObject { 117 | private static class SingleObjectHandler { 118 | private final static LazyHandlerSingleObject instance = new LazyHandlerSingleObject(); 119 | } 120 | private static LazyHandlerSingleObject instance; 121 | 122 | public static LazyHandlerSingleObject getInstance() { 123 | return SingleObjectHandler.instance; 124 | } 125 | } 126 | 127 | 我们知道,类在使用的时候才会被classloder加载,静态内部类`SingleObjectHandler`正是利用了这个特点,来懒加载其外部对象`LazyHandlerSingleObject`的实例。 128 | 129 | > 类嵌套一向是一种比较难以理解的概念,静态内部类是最简单的一种嵌套类,最好把他看作是普通的类,只是碰巧被声明在另一个类内部而已。唯一与普通类不同的是,静态内部类和其外部类可以互相访问所有成员,包括私有成员。 130 | 131 | 当第一次调用`getInstance()`方法的时候,`SingleObjectHandler`才第一次被使用到,从而加载它,自然也就创建了`LazyHandlerSingleObject`的实例,`SingleObjectHandler`通过`static`保证这个实例是唯一的。 132 | 133 | ## 枚举 134 | 这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。 135 | 136 | 这种方式是《Effective Java》作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。推荐在需要使用单例的时候使用这种方式。 137 | 138 | EnumSingleton.java 139 | 140 | public enum EnumSingleton { 141 | INSTANCE; 142 | public void doSomething() { 143 | ... 144 | } 145 | } 146 | 147 | 就是这么简单!直接使用`EnumSingleton.INSTANCE`就可以了,比如`EnumSingleton.INSTANCE.doSomething()`。Enum类型类似于类,也可以有成员变量和成员方法。 148 | 149 | # 总结 150 | 首先,请尝试使用枚举的方式来实现单例,枚举机制本身对单例有很好的支持。 151 | 152 | 如果觉得枚举方式不熟悉,那么: 153 | 154 | 通常,比较轻量的单例直接用饿汉式即可; 155 | 156 | 重量级的单例对象,最好通过懒加载的方式构建,根据线程安全性的要求选择以上两种懒汉式的方式;当然静态内部类也是一种不错的懒加载方式。 -------------------------------------------------------------------------------- /singleton/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | singleton 7 | 8 | 9 | com.getset.designpatterns 10 | get-designpatterns 11 | 1.0-SNAPSHOT 12 | ../ 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /singleton/src/main/java/com/getset/designpatterns/singleton/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.singleton; 2 | 3 | public class Client { 4 | public static void main(String[] args) { 5 | System.out.println(SingleObject.getInstance()); 6 | System.out.println(SingleObject.getInstance()); 7 | 8 | System.out.println(LazySingleObject.getInstance()); 9 | System.out.println(LazySingleObject.getInstance()); 10 | 11 | System.out.println(LazyHandlerSingleObject.getInstance()); 12 | System.out.println(LazyHandlerSingleObject.getInstance()); 13 | 14 | System.out.println(EnumSingleton.INSTANCE.hashCode()); 15 | System.out.println(EnumSingleton.INSTANCE.hashCode()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /singleton/src/main/java/com/getset/designpatterns/singleton/EnumSingleton.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.singleton; 2 | 3 | public enum EnumSingleton { 4 | INSTANCE; 5 | public void doSomething() { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /singleton/src/main/java/com/getset/designpatterns/singleton/LazyHandlerSingleObject.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.singleton; 2 | 3 | public class LazyHandlerSingleObject { 4 | private static class SingleObjectHandler { 5 | private final static LazyHandlerSingleObject instance = new LazyHandlerSingleObject(); 6 | } 7 | private static LazyHandlerSingleObject instance; 8 | 9 | public static LazyHandlerSingleObject getInstance() { 10 | return SingleObjectHandler.instance; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /singleton/src/main/java/com/getset/designpatterns/singleton/LazySingleObject.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.singleton; 2 | 3 | public class LazySingleObject { 4 | private static volatile LazySingleObject instance; 5 | private LazySingleObject() {} 6 | public static LazySingleObject getInstance() { 7 | if (instance == null) { 8 | synchronized (LazySingleObject.class) { 9 | if (instance == null) { 10 | instance = new LazySingleObject(); 11 | } 12 | } 13 | } 14 | return instance; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /singleton/src/main/java/com/getset/designpatterns/singleton/SingleObject.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.singleton; 2 | 3 | public class SingleObject { 4 | 5 | private final static SingleObject INSTANCE = new SingleObject(); 6 | 7 | private SingleObject() {} 8 | 9 | public static SingleObject getInstance() { 10 | return INSTANCE; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /state/README.md: -------------------------------------------------------------------------------- 1 | 在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。 2 | 3 | 我们程序猿在码代码的时候由于不同的工作状态,可能写出质量不一的代码,并我们不是AI嘛。 4 | 5 | * 早上上班的时候,休息了一夜,精力旺盛,程序猿能够高效码代码,bug也不多; 6 | * 当熬夜加班的时候,运转了一天的大脑无比疲惫,写出的代码常有bug出没; 7 | * 当然,我们也都非常期待在码代码的时候能够达到“天人合一”之境界(虽然这种状态通常出现在玩游戏时),没有烦人的会议、没有别人的打扰,虽然没有程序猿鼓励妹子在侧,却能几分钟写出通常需要几个小时才能写出的逻辑,解决近几天都未解决的问题,可谓“码神附体”! 8 | 9 | # 例子 10 | 11 | 如果上述情况用代码来表述的话,我们来设计一下。 12 | 13 | **程序猿还是同一个人,但是同一个任务在不同的状态下,可能会达到不同的效果。** 14 | 15 | 注意上边这句话,有两个“同一个”和两个”不同的“。 16 | 17 | 人和任务是相同的,是不变的部分;状态和效果是不同的,是变的部分。按照设计模式的套路,不变的和变化的是要分开来的,从而满足”开闭“原则,有利于扩展。 18 | 19 | 不变的是人(类`Developer`)和任务(方法`Developer.develop()`),变化的状态(类`XxxState`)作为`Developer`的成员变量能够随时切换看来就可以解决这个问题。 20 | 21 | 由于状态是变化的,因此需要不同的类`XxxState`来描述,其方法`XxxState.develop()`正好可以灵活实现不同状态下的工作效果。 22 | 23 | 完美!最后还有一个,按照设计模式的通常套路,同一类变化的类要抽象为接口或抽象类,并提供统一的接口方法,从而做到”面向接口“编程,以应对变化。 24 | 25 | 设计完毕,那就开工: 26 | 27 | Developer.java 28 | 29 | public class Developer { 30 | private State state; 31 | 32 | public Developer(State state) { 33 | this.state = state; 34 | } 35 | 36 | public void setState(State state) { 37 | this.state = state; 38 | } 39 | 40 | public void develop() { 41 | state.coding(); 42 | } 43 | } 44 | 45 | `Developer`维护有`State`的引用,并提供`setState(State)`方法切换状态。 46 | 47 | State.java 48 | 49 | public interface State { 50 | void coding(); 51 | } 52 | 53 | EffectiveState.java 54 | 55 | public class EffectiveState implements State { 56 | public void coding() { 57 | System.out.println("高效码代码,偶有bug,人非圣贤嘛~"); 58 | } 59 | } 60 | 61 | ExhaustedState.java 62 | 63 | public class ExhaustedState implements State { 64 | public void coding() { 65 | System.out.println("加班熬夜码代码,专业写bug"); 66 | } 67 | } 68 | 69 | TianRenHeYiState.java 70 | 71 | public class TianRenHeYiState implements State { 72 | public void coding() { 73 | System.out.println("写代码进入天人合一境界,仿佛三头六臂,码神附体"); 74 | } 75 | } 76 | 77 | ![状态模式](images/state.png) 78 | 79 | 不同的状态有不同的实现效果,通过`State`抽象出统一的接口。 80 | 81 | 测试一下: 82 | 83 | Client.java 84 | 85 | public class Client { 86 | public static void main(String[] args) { 87 | Developer developer = new Developer(new EffectiveState()); 88 | developer.develop(); 89 | developer.setState(new ExhaustedState()); 90 | developer.develop(); 91 | developer.setState(new TianRenHeYiState()); 92 | developer.develop(); 93 | } 94 | } 95 | 96 | 使用`Developer`的`setState`方法切换不同的状态,从而实现不同的操作: 97 | 98 | 高效码代码,偶有bug,人非圣贤嘛~ 99 | 加班熬夜码代码,专业写bug 100 | 写代码进入天人合一境界,仿佛三头六臂,码神附体 101 | 102 | # 总结 103 | 104 | 这就是状态模式,将不同的状态包装为不同的类,供其本体引用,从而实现灵活的状态切换。 105 | 106 | 看到这里其实感觉状态模式和策略模式很像,状态模式是将不同的状态对象作为成员变量给使用者(也称”环境“),策略模式是将不同的策略对象作为成员变量给使用者(也称”环境“)。其实二者还是有些使用上的区别的: 107 | 108 | * 策略模式中,作为成员变量的策略对象通常不会经常变化;而状态模式在使用者(也称”环境“)的整个生命周期中会不断变化。 109 | * 策略模式中,通常并不明确告诉客户端所选择的具体策略;而状态模式中,所处的状态是明确告知客户端的; 110 | * 通常策略模式的使用者自己选择一个具体策略;而状态模式的使用者(也称”环境“)通常是被动使用某种状态。 111 | 112 | **使用场景** 113 | 114 | * 一个对象的行为依赖于它所处的状态,对象的行为必须随着其状态的改变而改变; 115 | * 对象在某个方法里依赖一重或多重的条件转移语句,而且其中有大量代码的时候。 116 | 117 | **注意事项** 118 | 119 | * 状态模式并未规定哪个角色来进行状态切换,上边的例子是由使用者(也称”环境“)的`setState`方法来切换状态,而有些情况下是由”状态“对象本身来切换到下一个状态。 120 | * 使用者(也称”环境“)也可以把自己作为参数传递给状态对象,从而状态对象也可以调用使用者的方法。 -------------------------------------------------------------------------------- /state/images/state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/state/images/state.png -------------------------------------------------------------------------------- /state/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | get-designpatterns 7 | com.getset.designpatterns 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | state 13 | 14 | 15 | -------------------------------------------------------------------------------- /state/src/main/java/com/getset/designpatterns/state/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.state; 2 | 3 | public class Client { 4 | public static void main(String[] args) { 5 | Developer developer = new Developer(new EffectiveState()); 6 | developer.develop(); 7 | developer.setState(new ExhaustedState()); 8 | developer.develop(); 9 | developer.setState(new TianRenHeYiState()); 10 | developer.develop(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /state/src/main/java/com/getset/designpatterns/state/Developer.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.state; 2 | 3 | public class Developer { 4 | private State state; 5 | 6 | public Developer(State state) { 7 | this.state = state; 8 | } 9 | 10 | public void setState(State state) { 11 | this.state = state; 12 | } 13 | 14 | public void develop() { 15 | state.coding(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /state/src/main/java/com/getset/designpatterns/state/EffectiveState.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.state; 2 | 3 | public class EffectiveState implements State { 4 | public void coding() { 5 | System.out.println("高效码代码,偶有bug,人非圣贤嘛~"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /state/src/main/java/com/getset/designpatterns/state/ExhaustedState.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.state; 2 | 3 | public class ExhaustedState implements State { 4 | public void coding() { 5 | System.out.println("加班熬夜码代码,专业写bug"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /state/src/main/java/com/getset/designpatterns/state/State.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.state; 2 | 3 | public interface State { 4 | void coding(); 5 | } 6 | -------------------------------------------------------------------------------- /state/src/main/java/com/getset/designpatterns/state/TianRenHeYiState.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.state; 2 | 3 | public class TianRenHeYiState implements State { 4 | public void coding() { 5 | System.out.println("写代码进入天人合一境界,仿佛三头六臂,码神附体"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /strategy/README.md: -------------------------------------------------------------------------------- 1 | 策略(Strategy)模式是对算法的一种封装,是把使用算法的责任和算法本身分割开来,委托给不同的对象管理,从而可以实现算法的互换,从而一个类的行为或其算法可以在运行时更改,这种设计模式属于行为型模式。 2 | 3 | 策略模式在生活中和架构设计中非常常见。 4 | * 比如,商场打折,对于同样一类商品,不同的时间,促销方式和打折力度是不同的,这时候,促销方式和打折力度就是策略,对于这种应用场景来说,策略需要是可以随时变更的。 5 | * 还有个例子,比如排序,不同的对象,其排序依据不同。对于学生而言,有时候根据身高排序安排坐位,有时候根据成绩排序安排考场,这时候把这两种排序方式独立出来是一种比较好的方法,当需要安排坐位时,就使用“按照身高排序”的策略,当需要安排考场时就使用“按照成绩排序”的策略。从而一旦有新的需求和新的策略,只需要变更另外一个策略(比如“按年龄排序”)就OK了。 6 | * 再举个例子,说到插卡游戏机,估计不少朋友没见过,80后还是有印象的,游戏机本身不能玩,需要再购买不同的游戏卡插上才能玩,比如魂斗罗、超级玛丽等等。 7 | 8 | # 例子 9 | 10 | 我们用一个简单的计算的例子来阐述这种模式。需求很简单,提供两个数字,给出这两个数字的加减乘除的结果。 11 | 12 | 在这里,加减乘除就是不同的策略,我们统一用`Operation`抽象。 13 | 14 | Operation.java 15 | 16 | public interface Operation { 17 | int calculate(int x, int y); 18 | } 19 | 20 | AddOperation.java 21 | 22 | public class AddOperation implements Operation { 23 | public int calculate(int x, int y) { 24 | return x + y; 25 | } 26 | } 27 | 28 | SubOperation.java 29 | 30 | public class SubOperation implements Operation { 31 | public int calculate(int x, int y) { 32 | return x - y; 33 | } 34 | } 35 | 36 | MulOperation.java 37 | 38 | public class MulOperation implements Operation { 39 | public int calculate(int x, int y) { 40 | return x * y; 41 | } 42 | } 43 | 44 | MulOperation.java 45 | 46 | public class DivOperation implements Operation { 47 | public int calculate(int x, int y) { 48 | return x / y; 49 | } 50 | } 51 | 52 | 我们的计算器比较旧,来自上世纪,就像插卡游戏机,需要先设定计算方法,然后才能进行计算。 53 | 54 | Calculator.java 55 | 56 | public class Calculator { 57 | private Operation operation; 58 | 59 | public Calculator(Operation operation) { 60 | this.operation = operation; 61 | } 62 | 63 | public void setOperation(Operation operation) { 64 | this.operation = operation; 65 | } 66 | 67 | public int calculate(int x, int y) { 68 | return operation.calculate(x, y); 69 | } 70 | } 71 | 72 | 下面我们用一下这个计算器: 73 | 74 | Client.java 75 | 76 | public class Client { 77 | public static void main(String[] args) { 78 | int x = 6, y = 3; 79 | Calculator calculator = new Calculator(new AddOperation()); 80 | System.out.println("6+3=" + calculator.calculate(x, y)); 81 | calculator.setOperation(new SubOperation()); 82 | System.out.println("6-3=" + calculator.calculate(x, y)); 83 | calculator.setOperation(new MulOperation()); 84 | System.out.println("6*3=" + calculator.calculate(x, y)); 85 | calculator.setOperation(new DivOperation()); 86 | System.out.println("6/3=" + calculator.calculate(x, y)); 87 | } 88 | } 89 | 90 | 输出如下: 91 | 92 | 6+3=9 93 | 6-3=3 94 | 6*3=18 95 | 6/3=2 96 | 97 | # 总结 98 | 99 | 策略模式是如何做到随意切换的呢?很显然,仍然是采用了面向接口编程的思路,所有的策略都实现了同样的接口,策略消费者只需要基于接口调用即可,不同的接口实现提供了不同的策略。这也是“历史替换原则”的具体体现。 100 | 101 | 在具体的开发中,策略模式的例子经常见到: 102 | * 我们在使用SpringMVC开发的时候,前端页面的渲染可能采用不同的技术,比如JSP、Thymeleaf等,对于Spring框架来说,要能够接纳不同的页面渲染技术,因此提供了`ViewResolver`的接口,如果是JSP的渲染方法,那么就使用`InternalResourceViewResolver`;如果是Thymeleaf的渲染方法,就配置使用`ThymeleafViewResolver`。 103 | * Java中观察`Comparable`和`Comparator`两个接口的应用,也是理解策略模式的好思路。对于`Comparable`来说,其应用于被比较的对象上,从接口方法`int compareTo(T o);`可以看出,是由当前对象调起的与另一个对象的比较;而`Comparator`接口的用于比较的接口方法为`int compare(T o1, T o2);`,不同点在于参数个数,一个`Comparator`是独立于`o1`和`o2`两个被比较对象之外的第三方,因此做到了排序算法与调用算法的解耦,不同的`Comparator`实现就是不同的策略,可见`Comparator`的应用方式是策略模式。 104 | 105 | 最后总结一下策略模式的使用场景:1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 2、一个系统需要动态地在几种算法中选择一种。 3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。 106 | -------------------------------------------------------------------------------- /strategy/images/strategy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangeek/java-design-patterns-notes/f67a546852d11540e54d018031b0ad28169ef983/strategy/images/strategy.png -------------------------------------------------------------------------------- /strategy/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | strategy 8 | 9 | 10 | com.getset.designpatterns 11 | get-designpatterns 12 | 1.0-SNAPSHOT 13 | ../ 14 | 15 | 16 | -------------------------------------------------------------------------------- /strategy/src/main/java/com/getset/designpatterns/strategy/AddOperation.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.strategy; 2 | 3 | public class AddOperation implements Operation { 4 | public int calculate(int x, int y) { 5 | return x + y; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /strategy/src/main/java/com/getset/designpatterns/strategy/Calculator.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.strategy; 2 | 3 | public class Calculator { 4 | private Operation operation; 5 | 6 | public Calculator(Operation operation) { 7 | this.operation = operation; 8 | } 9 | 10 | public void setOperation(Operation operation) { 11 | this.operation = operation; 12 | } 13 | 14 | public int calculate(int x, int y) { 15 | return operation.calculate(x, y); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /strategy/src/main/java/com/getset/designpatterns/strategy/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.strategy; 2 | 3 | public class Client { 4 | public static void main(String[] args) { 5 | int x = 6, y = 3; 6 | Calculator calculator = new Calculator(new AddOperation()); 7 | System.out.println("6+3=" + calculator.calculate(x, y)); 8 | calculator.setOperation(new SubOperation()); 9 | System.out.println("6-3=" + calculator.calculate(x, y)); 10 | calculator.setOperation(new MulOperation()); 11 | System.out.println("6*3=" + calculator.calculate(x, y)); 12 | calculator.setOperation(new DivOperation()); 13 | System.out.println("6/3=" + calculator.calculate(x, y)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /strategy/src/main/java/com/getset/designpatterns/strategy/DivOperation.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.strategy; 2 | 3 | public class DivOperation implements Operation { 4 | public int calculate(int x, int y) { 5 | return x / y; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /strategy/src/main/java/com/getset/designpatterns/strategy/MulOperation.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.strategy; 2 | 3 | public class MulOperation implements Operation { 4 | public int calculate(int x, int y) { 5 | return x * y; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /strategy/src/main/java/com/getset/designpatterns/strategy/Operation.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.strategy; 2 | 3 | public interface Operation { 4 | int calculate(int x, int y); 5 | } 6 | -------------------------------------------------------------------------------- /strategy/src/main/java/com/getset/designpatterns/strategy/SubOperation.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.strategy; 2 | 3 | public class SubOperation implements Operation { 4 | public int calculate(int x, int y) { 5 | return x - y; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /template-method/README.md: -------------------------------------------------------------------------------- 1 | 在模板方法模式(Template Method Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。 2 | 3 | 关于模板,大家生活中都有体会: 4 | * 我们总感觉新闻联播里的新闻有些固定的“套路”,比如`______在____的陪同下,不远万里,来到_____家中,为_____带来了节日的祝福和良好的祝愿,并饶有兴致的观看了_____。____握着___的手激动的说_______`。 5 | * 我们学生期间填了各种表格,写了不少报告,比如实验报告:`1、实验目的和要求;2、实验设备(环境)及要求;3、实验步骤;4、实验结果;5、讨论和分析`。 6 | 7 | 这样的例子能举出好多来,这就是模板,里边的空或步骤是我们具体要去补充的地方。为什么要搞这些模板出来呢?**规范**,为了希望大家都按照一定的规范约束来实现。这也是我们在类关系的设计中,采用这种设计模式的初衷。 8 | 9 | # 例子 10 | 11 | 下边这个例子是通过学生或老师的自我介绍来演示模板方法模式的应用。 12 | 13 | 无论老师还是学生,在做自我介绍时通常都有自我基本情况介绍、获得过什么奖励、求学/教学经历等方面的内容,可以认为是一个模板。我们的例子中,自我介绍包含两部分,基本情况介绍和表决心(哈哈),如下: 14 | 15 | SchoolPerson.java 16 | 17 | public abstract class SchoolPerson { 18 | protected String name; 19 | protected int age; 20 | protected String schoolName; 21 | protected String hometown; 22 | 23 | public SchoolPerson(String name, int age, String schoolName, String hometown) { 24 | this.name = name; 25 | this.age = age; 26 | this.schoolName = schoolName; 27 | this.hometown = hometown; 28 | } 29 | 30 | public void selfIntroduction() { 31 | myBasicInfo(); 32 | myslogan(); 33 | } 34 | 35 | public abstract void myBasicInfo(); 36 | 37 | public abstract void mySlogan(); 38 | } 39 | 40 | 我们看到,这个模板如果作为填空题的话,“空”就在`myBasicInfo`和`mySlogan`两个方法上。这两个方法是抽象的,要求子类去实现。 41 | 42 | Student.java 43 | 44 | public class Student extends SchoolPerson { 45 | public Student(String name, int age, String schoolName, String hometown) { 46 | super(name, age, schoolName, hometown); 47 | } 48 | 49 | public void myBasicInfo() { 50 | System.out.println("我是一名学生,名叫" + this.name + ", 今年" + this.age + "岁, " + this.hometown + "人, 在" + this.schoolName + "上学。"); 51 | } 52 | 53 | public void mySlogan() { 54 | System.out.println("在我在" + this.schoolName + "求学的过程中,我一定 好好学习,天天向上!"); 55 | } 56 | } 57 | 58 | Teacher.java 59 | 60 | public class Teacher extends SchoolPerson { 61 | 62 | public Teacher(String name, int age, String schoolName, String hometown) { 63 | super(name, age, schoolName, hometown); 64 | } 65 | 66 | public void myBasicInfo() { 67 | System.out.println("我是一名教师,名叫" + this.name + ", 今年" + this.age + "岁, " + this.hometown + "人, 在" + this.schoolName + "教书。"); 68 | } 69 | 70 | public void mySlogan() { 71 | System.out.println("在我在" + this.schoolName + "教学的过程中,我一定 为人师表,诲人不倦!"); 72 | } 73 | } 74 | 75 | 学生和老师对两个模板方法都有不同的实现。我们看一下执行效果: 76 | 77 | Client.java 78 | 79 | public class Client { 80 | public static void main(String[] args) { 81 | SchoolPerson student = new Student("张三", 12, "光明小学", "山东济南"); 82 | student.selfIntroduction(); 83 | 84 | SchoolPerson teacher = new Teacher("李四", 32, "光明小学", "山东青岛"); 85 | teacher.selfIntroduction(); 86 | } 87 | } 88 | 89 | 输出为: 90 | 91 | 我是一名学生,名叫张三, 今年12岁, 山东济南人, 在光明小学上学。 92 | 在我在光明小学求学的过程中,我一定 好好学习,天天向上! 93 | 我是一名教师,名叫李四, 今年32岁, 山东青岛人, 在光明小学教书。 94 | 在我在光明小学教学的过程中,我一定 为人师表,诲人不倦! 95 | 96 | # 总结 97 | 98 | 看这个例子,你可能会感觉,这就是继承嘛,有啥新鲜的?的确,虽然我们以前一直说类的组合(或说委托)优先于类的继承来使用。但是模板方法模式是为数不多的为我们示范关于继承的使用方式的设计模式。 99 | 100 | 模板方法模式的特点很好总结,它将一般性的可复用的行为由基类固化,而把特殊化的行为交由具体的子类来实现。具体来说: 101 | 1. 子类通常不关心全局(比如整个流程、提纲、步骤),而只负责”填空“;”填空“通过实现或重写父类的方法来实现。 102 | 2. 从父类角度,全局性的规范约束掌握在自己手中,具体来说通过模板方法来约束,从而能够尽量简化子类的复杂度。父类并不一定是抽象类,模板方法也并不一定是抽象方法。 103 | 104 | 再简单介绍一个实际的例子。我们知道,Servlet有特定的规范,以便Servlet的容器(比如tomcat)和Servlet的实现(我们开发的JavaEE应用)能够很好的合作。 105 | 106 | 其中`javax.servlet.http.HttpServlet`就是Servlet规范在Http协议方面的”践行者“。`HttpServlet`也是一种`Servlet`,因此也必须有`service`方法来提供服务。但是Http有其具体的服务分类(GET、POST、PUT等不同的请求方法),我们看一下`HttpServlet.service`方法的实现: 107 | 108 | HttpServlet.java 109 | 110 | protected void service(HttpServletRequest req, HttpServletResponse resp) 111 | throws ServletException, IOException 112 | { 113 | String method = req.getMethod(); 114 | 115 | if (method.equals(METHOD_GET)) { 116 | ... 117 | doGet(req, resp); 118 | ... 119 | 120 | } else if (method.equals(METHOD_HEAD)) { 121 | ... 122 | doHead(req, resp); 123 | 124 | } else if (method.equals(METHOD_POST)) { 125 | doPost(req, resp); 126 | 127 | } else if (method.equals(METHOD_PUT)) { 128 | doPut(req, resp); 129 | 130 | } else if (method.equals(METHOD_DELETE)) { 131 | doDelete(req, resp); 132 | 133 | } else if (method.equals(METHOD_OPTIONS)) { 134 | doOptions(req,resp); 135 | 136 | } else if (method.equals(METHOD_TRACE)) { 137 | doTrace(req,resp); 138 | 139 | } else { 140 | ... 141 | } 142 | } 143 | 144 | 简单起见,用省略号代替了一些代码,可以看到,service接到请求后,会先判断请求方法的类型,如果是GET请求就交给doGet去实现,如果是POST请求就交给doPost去实现。 145 | 146 | 对于一个具体的servlet(`ConcreteHttpServlet`)来说,只需要继承`HttpServlet`并重写具体的方法就可以了。比如某个Servlet是用来处理GET请求的,那么只重写`doGet`方法填写处理逻辑就OK了。其它的处理不用care。 147 | 148 | 不过现在由于Spring等Web框架的出现,我们不需要一个一个servlet去写JavaWeb程序,而是由比如Spring的`DispatcherServlet`这样的统一的Servlet入口来处理了,它直接映射了”/"这样的请求URL,从而托管了所有的请求。但是既然是JavaEE框架,还是要遵循Servlet规范的,`DispatcherServlet`的父类`FrameworkServlet`就对`doGet`、`doPost`等模板方法进行了重写,以满足规范的要求。 -------------------------------------------------------------------------------- /template-method/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | template-method 8 | 9 | 10 | com.getset.designpatterns 11 | get-designpatterns 12 | 1.0-SNAPSHOT 13 | ../ 14 | 15 | 16 | -------------------------------------------------------------------------------- /template-method/src/main/java/com/getset/designpatterns/templatemethod/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.templatemethod; 2 | 3 | public class Client { 4 | public static void main(String[] args) { 5 | SchoolPerson student = new Student("张三", 12, "光明小学", "山东济南"); 6 | student.selfIntroduction(); 7 | 8 | SchoolPerson teacher = new Teacher("李四", 32, "光明小学", "山东青岛"); 9 | teacher.selfIntroduction(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /template-method/src/main/java/com/getset/designpatterns/templatemethod/SchoolPerson.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.templatemethod; 2 | 3 | public abstract class SchoolPerson { 4 | protected String name; 5 | protected int age; 6 | protected String schoolName; 7 | protected String hometown; 8 | 9 | public SchoolPerson(String name, int age, String schoolName, String hometown) { 10 | this.name = name; 11 | this.age = age; 12 | this.schoolName = schoolName; 13 | this.hometown = hometown; 14 | } 15 | 16 | public void selfIntroduction() { 17 | myBasicInfo(); 18 | mySlogan(); 19 | } 20 | 21 | public abstract void myBasicInfo(); 22 | 23 | public abstract void mySlogan(); 24 | } 25 | -------------------------------------------------------------------------------- /template-method/src/main/java/com/getset/designpatterns/templatemethod/Student.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.templatemethod; 2 | 3 | public class Student extends SchoolPerson { 4 | public Student(String name, int age, String schoolName, String hometown) { 5 | super(name, age, schoolName, hometown); 6 | } 7 | 8 | public void myBasicInfo() { 9 | System.out.println("我是一名学生,名叫" + this.name + ", 今年" + this.age + "岁, " + this.hometown + "人, 在" + this.schoolName + "上学。"); 10 | } 11 | 12 | public void mySlogan() { 13 | System.out.println("在我在" + this.schoolName + "求学的过程中,我一定 好好学习,天天向上!"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /template-method/src/main/java/com/getset/designpatterns/templatemethod/Teacher.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.templatemethod; 2 | 3 | public class Teacher extends SchoolPerson { 4 | 5 | public Teacher(String name, int age, String schoolName, String hometown) { 6 | super(name, age, schoolName, hometown); 7 | } 8 | 9 | public void myBasicInfo() { 10 | System.out.println("我是一名教师,名叫" + this.name + ", 今年" + this.age + "岁, " + this.hometown + "人, 在" + this.schoolName + "教书。"); 11 | } 12 | 13 | public void mySlogan() { 14 | System.out.println("在我在" + this.schoolName + "教学的过程中,我一定 为人师表,诲人不倦!"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /visitor/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | get-designpatterns 7 | com.getset.designpatterns 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | visitor 13 | 14 | 15 | -------------------------------------------------------------------------------- /visitor/src/main/java/com/getset/designpatterns/visitor/Area.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.visitor; 2 | 3 | public class Area implements Calculator { 4 | public double ofShape(Triangle triangle) { 5 | double a = triangle.getEdgeA(), b = triangle.getEdgeB(), c = triangle.getEdgeC(); 6 | double p = (a + b + c) / 2; 7 | return Math.sqrt(p * (p - a) * (p - b) * (p - c)); 8 | } 9 | 10 | public double ofShape(Circle circle) { 11 | return Math.PI * circle.getRadius() * circle.getRadius(); 12 | } 13 | 14 | public double ofShape(Square square) { 15 | return Math.pow(square.getEdge(), 2); 16 | } 17 | 18 | public double ofShape(Rectangle rectangle) { 19 | return rectangle.getWidth() * rectangle.getLength(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /visitor/src/main/java/com/getset/designpatterns/visitor/Calculator.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.visitor; 2 | 3 | public interface Calculator { 4 | double ofShape(Triangle triangle); 5 | double ofShape(Circle circle); 6 | double ofShape(Square square); 7 | double ofShape(Rectangle rectangle); 8 | } 9 | -------------------------------------------------------------------------------- /visitor/src/main/java/com/getset/designpatterns/visitor/Circle.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.visitor; 2 | 3 | public class Circle implements Shape { 4 | private double radius; 5 | 6 | public Circle(double radius) { 7 | this.radius = radius; 8 | } 9 | 10 | public double getRadius() { 11 | return radius; 12 | } 13 | 14 | public double accept(Calculator calculator) { 15 | return calculator.ofShape(this); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /visitor/src/main/java/com/getset/designpatterns/visitor/Client.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.visitor; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Client { 7 | public static void main(String[] args) { 8 | // 一个含有5个元素的List,包含三种不同的形状 9 | List shapes = new ArrayList(); 10 | shapes.add(new Triangle(1.3, 2.2, 3.1)); 11 | shapes.add(new Circle(1.2)); 12 | shapes.add(new Triangle(2.4, 3.3, 4.2)); 13 | shapes.add(new Rectangle(2.1, 3.2)); 14 | shapes.add(new Circle(5.6)); 15 | 16 | // 计算周长和面积的不同策略(访问者) 17 | Perimeter perimeter = new Perimeter(); 18 | Area area = new Area(); 19 | 20 | // 将周长和面积的计算策略传入(接受不同访问者的访问) 21 | for (Shape shape : shapes) { 22 | System.out.printf("周长 : %5.2f\t 面积 : %5.2f\n", shape.accept(perimeter), shape.accept(area)); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /visitor/src/main/java/com/getset/designpatterns/visitor/Perimeter.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.visitor; 2 | 3 | public class Perimeter implements Calculator { 4 | public double ofShape(Triangle triangle) { 5 | return triangle.getEdgeA() + triangle.getEdgeB() + triangle.getEdgeC(); 6 | } 7 | 8 | public double ofShape(Circle circle) { 9 | return circle.getRadius() * Math.PI * 2; 10 | } 11 | 12 | public double ofShape(Square square) { 13 | return square.getEdge() * 4; 14 | } 15 | 16 | public double ofShape(Rectangle rectangle) { 17 | return 2 * (rectangle.getLength() + rectangle.getWidth()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /visitor/src/main/java/com/getset/designpatterns/visitor/Rectangle.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.visitor; 2 | 3 | public class Rectangle implements Shape { 4 | private double length; 5 | private double width; 6 | 7 | public Rectangle(double length, double width) { 8 | this.length = length; 9 | this.width = width; 10 | } 11 | 12 | public double getLength() { 13 | return length; 14 | } 15 | 16 | public double getWidth() { 17 | return width; 18 | } 19 | 20 | public double accept(Calculator calculator) { 21 | return calculator.ofShape(this); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /visitor/src/main/java/com/getset/designpatterns/visitor/Shape.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.visitor; 2 | 3 | public interface Shape { 4 | double accept(Calculator calculator); 5 | } 6 | -------------------------------------------------------------------------------- /visitor/src/main/java/com/getset/designpatterns/visitor/Square.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.visitor; 2 | 3 | public class Square implements Shape { 4 | private double edge; 5 | 6 | public Square(double edge) { 7 | this.edge = edge; 8 | } 9 | 10 | public double getEdge() { 11 | return edge; 12 | } 13 | 14 | public double accept(Calculator calculator) { 15 | return calculator.ofShape(this); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /visitor/src/main/java/com/getset/designpatterns/visitor/Triangle.java: -------------------------------------------------------------------------------- 1 | package com.getset.designpatterns.visitor; 2 | 3 | public class Triangle implements Shape { 4 | private double edgeA; 5 | private double edgeB; 6 | private double edgeC; 7 | 8 | public Triangle(double edgeA, double edgeB, double edgeC) { 9 | this.edgeA = edgeA; 10 | this.edgeB = edgeB; 11 | this.edgeC = edgeC; 12 | } 13 | 14 | public double getEdgeA() { 15 | return edgeA; 16 | } 17 | 18 | public double getEdgeB() { 19 | return edgeB; 20 | } 21 | 22 | public double getEdgeC() { 23 | return edgeC; 24 | } 25 | 26 | public double accept(Calculator calculator) { 27 | return calculator.ofShape(this); 28 | } 29 | } 30 | --------------------------------------------------------------------------------