原型模式, Prototype Pattern

原型模式(Prototype Pattern)是一种创建型模式,通过复制(克隆)一个已有对象来创建新对象,而不是通过 new 重新实例化。 核心思想 当创建一个对象的代价较大(如需要复杂初始化、数据库查询、网络请求等)时,可以先创建一个原型对象,后续通过克隆该原型来快速获得新对象。 角色 Prototype(抽象原型):声明克隆方法 clone()。 ConcretePrototype(具体原型):实现克隆方法,返回自身的副本。 Client(客户端):通过调用原型的克隆方法来创建新对象。 浅克隆与深克隆 浅克隆(Shallow Clone) 深克隆(Deep Clone) 基本类型字段 复制值 复制值 引用类型字段 复制引用(共享对象) 递归复制,独立对象 修改互不影响 ❌ 引用字段会互相影响 ✅ 完全独立 示例:细胞克隆 Java 通过实现 Cloneable 接口来支持浅克隆: public class Cell implements Cloneable { private String cellWall; // 细胞壁 private String cellMembrane; // 细胞膜 private String cellularTissue; // 细胞组织 public String getCellWall() { return cellWall; } public void setCellWall(String cellWall) { this.cellWall = cellWall; } // 其他 getter/setter 省略 @Override public Cell clone() { try { return (Cell) super.clone(); } catch (CloneNotSupportedException e) { throw new InternalError(e.getMessage()); } } } 客户端使用: ...

2026-04-16 · 1 min · 192 words · -

中介者模式, Mediator Pattern

中介者模式(Mediator Pattern)定义一个中介对象来封装一系列对象之间的交互,使各对象不需要显式地相互引用,从而降低耦合度,并可以独立地改变它们之间的交互。 核心思想 当系统中多个对象之间存在复杂的多对多交互时,对象之间的依赖关系会形成网状结构,难以维护。中介者模式将这种网状结构转化为星形结构——所有对象只与中介者通信,由中介者协调各对象之间的交互。 # 不使用中介者:网状结构 A ←→ B A ←→ C B ←→ C B ←→ D ... # 使用中介者:星形结构 A → Mediator ← B ↑ ↓ C D 角色 Mediator(抽象中介者):定义各同事对象通信的接口。 ConcreteMediator(具体中介者):实现协调逻辑,持有所有同事对象的引用。 Colleague(同事类):每个同事只知道中介者,通过中介者与其他同事交互。 示例:聊天室 聊天室是中介者模式的典型场景:用户之间不直接通信,而是通过聊天室(中介者)转发消息。 抽象中介者: public interface ChatRoom { void sendMessage(String message, User sender); void addUser(User user); } 具体中介者: import java.util.ArrayList; import java.util.List; public class ChatRoomImpl implements ChatRoom { private final List<User> users = new ArrayList<>(); @Override public void addUser(User user) { users.add(user); } @Override public void sendMessage(String message, User sender) { for (User user : users) { if (user != sender) { user.receive(message, sender.getName()); } } } } 同事类: ...

2026-04-16 · 2 min · 266 words · -

责任链模式, Chain of Responsibility Pattern

责任链模式(Chain of Responsibility)将请求沿着一条处理者链传递,每个处理者决定是否处理该请求,或将其传递给链上的下一个处理者。请求的发送者无需知道最终由哪个处理者来处理,从而实现了发送者与接收者的解耦。 核心思想 不使用责任链时,请求方往往需要硬编码逻辑来判断应该由哪个对象处理,导致紧耦合和大量条件判断。责任链将这些处理者串联起来,请求沿链传递,直到被某个处理者处理(或到达链尾)。 角色 Handler(抽象处理者):定义处理请求的接口,通常包含设置下一个处理者的方法。 ConcreteHandler(具体处理者):实现处理逻辑,决定自己是否处理请求,如果不处理则转发给下一个处理者。 Client(客户端):构建责任链并向链头发起请求。 示例:请假审批 不同级别的请假天数由不同层级的领导审批:组长审批 1 天以内,经理审批 3 天以内,总监审批 7 天以内,超过 7 天不予批准。 抽象处理者: public abstract class Approver { protected Approver next; public Approver setNext(Approver next) { this.next = next; return next; } public abstract void approve(int days); } 具体处理者: public class TeamLeader extends Approver { @Override public void approve(int days) { if (days <= 1) { System.out.println("组长批准了 " + days + " 天假。"); } else if (next != null) { next.approve(days); } } } public class Manager extends Approver { @Override public void approve(int days) { if (days <= 3) { System.out.println("经理批准了 " + days + " 天假。"); } else if (next != null) { next.approve(days); } } } public class Director extends Approver { @Override public void approve(int days) { if (days <= 7) { System.out.println("总监批准了 " + days + " 天假。"); } else { System.out.println("请假 " + days + " 天,超出审批权限,不予批准。"); } } } 客户端构建链并发起请求: ...

2026-04-16 · 2 min · 237 words · -

状态模式, State Pattern

状态模式(State Pattern)允许一个对象在其内部状态发生改变时改变其行为,使该对象看起来像是改变了它的类。 核心思想 在不使用状态模式的情况下,对象的行为往往通过大量 if-else 或 switch 判断当前状态来分支处理,导致代码臃肿、难以扩展。状态模式将每种状态封装成独立的类,对象将行为委托给当前状态对象,状态切换时只需替换状态对象即可。 角色 Context(上下文):持有当前状态的引用,对外暴露行为接口,将行为委托给当前状态对象处理。 State(抽象状态):定义所有具体状态共同的接口。 ConcreteState(具体状态):实现 State 接口,封装该状态下的行为,并在必要时触发状态转换。 示例:交通灯 交通灯有红、黄、绿三种状态,不同状态下的行为不同,且状态之间按规则流转。 抽象状态接口: public interface TrafficLightState { void handle(TrafficLight light); String getColor(); } 具体状态类: public class RedState implements TrafficLightState { @Override public void handle(TrafficLight light) { System.out.println("红灯停。"); light.setState(new GreenState()); } @Override public String getColor() { return "红"; } } public class GreenState implements TrafficLightState { @Override public void handle(TrafficLight light) { System.out.println("绿灯行。"); light.setState(new YellowState()); } @Override public String getColor() { return "绿"; } } public class YellowState implements TrafficLightState { @Override public void handle(TrafficLight light) { System.out.println("黄灯警示,请注意。"); light.setState(new RedState()); } @Override public String getColor() { return "黄"; } } Context(上下文)类: ...

2026-04-16 · 2 min · 224 words · -

策略模式, Strategy Pattern

策略模式(Strategy Pattern)定义了一系列算法,把这些算法一个个封装成单独的类,使它们可以相互替换,且算法的改变不会影响使用算法的客户。 策略模式重点是封装不同的算法和行为,不同的场景下可以相互替换。策略模式是开闭原则的体现——对扩展开放,对修改关闭:新增策略时不影响其他类,且场景类只依赖抽象而不依赖具体实现。 示例:字符串替换策略 这里以字符串替换为例:读取一个文件后,需要替换其中的变量再输出。替换方式可能有多种,我们用策略模式来支持运行时切换。 首先,建立抽象策略类 RepTempRule,定义公用变量和方法: public abstract class RepTempRule { protected String oldString = ""; public void setOldString(String oldString) { this.oldString = oldString; } protected String newString = ""; public String getNewString() { return newString; } public abstract void replace() throws Exception; } 现在有两个具体策略:将文本中的 aaa 替换成 bbbb,或替换成 ccc: public class RepTempRuleOne extends RepTempRule { @Override public void replace() throws Exception { newString = oldString.replaceFirst("aaa", "bbbb"); System.out.println("this is replace one"); } } public class RepTempRuleTwo extends RepTempRule { @Override public void replace() throws Exception { newString = oldString.replaceFirst("aaa", "ccc"); System.out.println("this is replace two"); } } 算法解决类,提供运行时自由选择和切换算法的能力: ...

2026-04-16 · 1 min · 210 words · -

设计模式 – 模板方法, Template Method

模板方法模式是类的行为模式。准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。这就是模板方法模式的用意。 模式结构 模板方法模式是所有模式中最为常见的几个模式之一,是基于继承的代码复用的基本技术。 模板方法模式需要开发抽象类和具体子类的设计师之间的协作。一个设计师负责给出一个算法的轮廓和骨架,另一些设计师则负责给出这个算法的各个逻辑步骤。代表这些具体逻辑步骤的方法称做基本方法(primitive method);而将这些基本方法汇总起来的方法叫做模板方法(template method),这个设计模式的名字就是从此而来。 模板方法所代表的行为称为顶级行为,其逻辑称为顶级逻辑。 这里涉及到两个角色: 抽象模板(Abstract Template) 角色的责任: 定义了一个或多个抽象操作,以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤。 定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。 具体模板(Concrete Template) 角色的责任: 实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤。 每一个抽象模板角色都可以有任意多个具体模板角色与之对应,而每一个具体模板角色都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。 基础示例 抽象模板角色类,abstractMethod()、hookMethod() 等基本方法是顶级逻辑的组成步骤,这个顶级逻辑由 templateMethod() 方法代表。 public abstract class AbstractTemplate { /** * 模板方法 */ public void templateMethod() { // 调用基本方法 abstractMethod(); hookMethod(); concreteMethod(); } /** * 基本方法的声明(由子类实现) */ protected abstract void abstractMethod(); /** * 基本方法(空方法) */ protected void hookMethod() {} /** * 基本方法(已经实现) */ private final void concreteMethod() { // 业务相关的代码 } } 具体模板角色类实现了父类所声明的基本方法,abstractMethod() 方法所代表的就是强制子类实现的剩余逻辑,而 hookMethod() 方法是可选择实现的逻辑。 ...

2026-04-16 · 3 min · 481 words · -

代理模式, proxy pattern

代理模式, proxy pattern 为其他对象提供一种代理以控制对这个对象的访问。 代理模式,即Proxy,它和 Adapter 模式很类似。我们先回顾Adapter模式,它用于把A接口转换为B接口: Adapter 模式 public BAdapter implements B { private A a; public BAdapter(A a) { this.a = a; } public void b() { a.a(); } } Proxy模式 而Proxy模式不是把A接口转换成B接口,它还是转换成A接口: public AProxy implements A { private A a; public AProxy(A a) { this.a = a; } public void a() { this.a.a(); } } 看起来 Proxy 只是把 A 接口又包了一层,这有什么意义呢? ...

2026-04-16 · 1 min · 117 words · -

创建者模式, 建造者模式, Builder

创建者模式, 建造者模式, Builder 定义: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示 使用场景 多个部件或零件,都可以装配到一个对象中,但产生的结果又不相同时。 当初始化一个对象特别复杂的时候,比如参数多,而且很多参数都有默认值。 它分为抽象建造者 (Builder) 角色、具体建造者 (ConcreteBuilder) 角色、导演者 (Director) 角色、产品 (Product) 角色四个角色。 抽象建造者 (Builder) 角色: 给 出一个抽象接口,以规范产品对象的各个组成成分的建造。 具体建造者 (ConcreteBuilder) 角色: 要完成的任务包括: 1.实现抽象建造者Builder所声明的接口,给出一步一步地完成创建产品实例的操作。2.在建造过程完成后,提供产品的实例。 导演者 (Director) 角色: 担任这个角色的类调用具体建造者角色以创建产品对象。 产品 (Product) 角色: 产品便是建造中的复杂对象。 对于Builder模式很简单,但是一直想不明白为什么要这么设计,为什么要向builder要Product而不是向知道建造过程的Director要。刚才google到一篇文章,总算清楚了。在这里转贴一下这位richardluo的比喻。 简单地说,就好象我要装修一套房子,可是我不懂施工——水电怎么走、木作怎么做,每个工种都有自己专注的领域;也不懂整体设计——用什么风格、先做哪道工序、各部分如何协调,这是设计师的职责。于是我需要找一支装修队,各工种各司其职;还得找个设计师,他来规划整体方案、协调各工种按图施工,而设计师本身不动手,只负责下命令。最后,我可以向工人要装修好的房间了。在这个过程中,设计师手里什么都没有,只有图纸和指令,所以要房间也是跟工人要,记住了! 以下是richardluo的代码,我根据他的思路加上了相应的注释。 定义工人接口,即一支装修队所需具备的通用技能。 // 工人接口。水电工负责布线,木工负责木作,油漆工负责涂装。 // 装修完成后,由装修队把房间交还给房主。 public interface Builder { void doElectrical(); // 水电工 void doCarpentry(); // 木工 void doPainting(); // 油漆工 Room getRoom(); } 定义设计师(Director),他持有图纸,协调装修队按方案施工。 // 设计师(Director)。持有图纸,规定施工顺序,但不亲自动手。 public class Designer { public void coordinate(Builder builder) { builder.doElectrical(); builder.doCarpentry(); builder.doPainting(); } } 装修队(ConcreteBuilder),负责具体的施工实施。 // 各专职工人 class ElectricalWorker { public String work() { return "electrical done"; } } class Carpenter { public String work() { return "carpentry done"; } } class Painter { public String work() { return "painting done"; } } // 装修队(ConcreteBuilder)。由水电工、木工、油漆工组成,各司其职。 // 装修完成后,由装修队把房间交还给房主。 public class WorkerTeam implements Builder { private ElectricalWorker electrician = new ElectricalWorker(); private Carpenter carpenter = new Carpenter(); private Painter painter = new Painter(); private String electrical = ""; private String carpentry = ""; private String painting = ""; public void doElectrical() { electrical = electrician.work(); } public void doCarpentry() { carpentry = carpenter.work(); } public void doPainting() { painting = painter.work(); } // 把装修好的房间交还给房主 public Room getRoom() { if (!electrical.equals("") && !carpentry.equals("") && !painting.equals("")) { System.out.println("room ready!"); return new Room(); } else { return null; } } } 房主(Client),雇人、收房。 // 房主。聘请一支装修队和一位设计师,让设计师协调装修队按图施工,最后从装修队手上收房。 public class Client { public static void main(String[] args) { Builder team = new WorkerTeam(); Designer designer = new Designer(); designer.coordinate(team); team.getRoom(); } } 好了,我觉得这样大概能说明白了。不知各位觉得如何呢?或者有更好的应用场景解释,敬请赐教。 ...

2026-04-16 · 4 min · 730 words · -

SOLID 面向对象设计原则

SOLID 是面向对象设计的五大基本原则的首字母缩写,由 Robert C. Martin(Uncle Bob)整理归纳。这五个原则是编写可维护、可扩展代码的基础。 S — 单一职责原则 (Single Responsibility Principle, SRP) 一个类应该只有一个引起它变化的原因。 一个类只做一件事。如果一个类承担了多个职责,那么每个职责的变化都可能影响这个类,导致它越来越难以维护。 反例: 简单工厂模式中的工厂类 Driver 同时负责判断车型和创建所有车对象,违反了单一职责。 O — 开闭原则 (Open-Closed Principle, OCP) 软件实体应该对扩展开放,对修改封闭。 添加新功能时,应该通过新增代码实现,而不是修改已有代码。详见开闭原则。 反例: 简单工厂的 if/else 判断链,每新增一种产品都要修改工厂方法。 L — 里氏替换原则 (Liskov Substitution Principle, LSP) 子类必须能够替换其父类,且程序行为不变。 任何使用父类的地方,换成子类后程序应该仍然正确运行。子类不应该破坏父类的契约。 示例: Benze 和 Bmw 都实现了 Car 接口,客户端代码使用 Car 类型,无论实际是哪种车,drive() 都能正确调用。 I — 接口隔离原则 (Interface Segregation Principle, ISP) 客户端不应该被迫依赖它不使用的方法。 接口应该尽量细化,不要把不相关的方法放在同一个接口里。大接口应该拆分成多个小接口,让实现类只需要关心自己用到的方法。 反例: 一个包含 fly()、swim()、run() 的 Animal 接口,鱼类被迫实现 fly() 和 run()。 D — 依赖倒置原则 (Dependency Inversion Principle, DIP) 高层模块不应该依赖低层模块,两者都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。 ...

2026-04-16 · 1 min · 87 words · -

开闭原则

开闭原则 开闭原则(Open-Close Principle/OCP)是面向对象设计中"可复用设计"的基石,是面向对象设计中最重要的原则之一,其它很多的设计原则都是实现开闭原则的一种手段。 1988年,Bertrand Meyer在他的著作《Object Oriented Software Construction》中提出了开闭原则,它的原文是这样: “Software entities should be open for extension,but closed for modification”。翻译过来就是: “软件实体应当对扩展开放,对修改关闭”。这句话说得略微有点专业,我们把它讲得更通俗一点,也就是: 软件系统中包含的各种组件,例如模块 (Modules) 、类 (Classes) 以及功能 (Functions) 等等,应该在不修改现有代码的基础上,引入新功能。开闭原则中"开",是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的;开闭原则中"闭",是指对于原有代码的修改是封闭的,即不应该修改原有的代码。实现开闭原则的关键就在于"抽象"。把系统的所有可能的行为抽象成一个抽象底层,这个抽象底层规定出所有的具体实现必须提供的方法的特征。作为系统设计的抽象层,要预见所有可能的扩展,从而使得在任何扩展情况下,系统的抽象底层不需修改;同时,由于可以从抽象底层导出一个或多个新的具体实现,可以改变系统的行为,因此系统设计对扩展是开放的。 我们在软件开发的过程中,一直都是提倡需求导向的。这就要求我们在设计的时候,要非常清楚地了解用户需求,判断需求中包含的可能的变化,从而明确在什么情况下使用开闭原则。 关于系统可变的部分,还有一个更具体的对可变性封装原则 (Principle of Encapsulation of Variation, EVP) ,它从软件工程实现的角度对开闭原则进行了进一步的解释。EVP要求在做系统设计的时候,对系统所有可能发生变化的部分进行评估和分类,每一个可变的因素都单独进行封装。 我们在实际开发过程的设计开始阶段,就要罗列出来系统所有可能的行为,并把这些行为加入到抽象底层,根本就是不可能的,这么去做也是不经济的,费时费力。另外,在设计开始阶段,对所有的可变因素进行预计和封装也不太现实,也是很难做得到。所以,开闭原则描绘的愿景只是一种理想情况或是极端状态,现实世界中是很难被完全实现的。我们只能在某些组件,在某种程度上符合开闭原则的要求。 通过以上的分析,对于开闭原则,我们可以得出这样的结论: 虽然我们不可能做到百分之百的封闭,但是在系统设计的时候,我们还是要尽量做到这一点。 对于软件系统的功能扩展,我们可以通过继承、重载或者委托等手段实现。以接口为例,它对修改就是是封闭的,而对具体的实现是开放的,我们可以根据实际的需要提供不同的实现,所以接口是符合开闭原则的。如果一个软件系统符合开闭原则的,那么从软件工程的角度来看,它至少具有这样的好处: 可复用性好。 我们可以在软件完成以后,仍然可以对软件进行扩展,加入新的功能,非常灵活。因此,这个软件系统就可以通过不断地增加新的组件,来满足不断变化的需求。 可维护性好。 由于对于已有的软件系统的组件,特别是它的抽象底层不去修改,因此,我们不用担心软件系统中原有组件的稳定性,这就使变化中的软件系统有一定的稳定性和延续性。开闭原则具有理想主义的色彩,它是面向对象设计的终极目标。因此,针对开闭原则的实现方法,一直都有面向对象设计的大师费尽心机,研究开闭原则的实现方式。后面要提到的里氏代换原则 (LSP) 、依赖倒转原则 (DIP) 、接口隔离原则 (ISP) 以及抽象类 (Abstract Class) 、接口(Interface)等等,都可以看作是开闭原则的实现方法。 http://baike.baidu.com/view/866233.htm

2026-04-16 · 1 min · 53 words · -

设计模式 — 工厂模式, Factory

设计模式 — 工厂模式, Factory 一、引子 话说十年前,有一个老板,他家有三辆汽车 —— Benz奔驰、Bmw宝马、Audi奥迪,还雇了司机为他开车。不过,老板坐车时总是怪怪的: 上Benz车后跟司机说"开奔驰车!",坐上Bmw后他说"开宝马车!",坐上Audi说"开奥迪车!"。你一定说: 这人有病!直接说开车不就行了?! 而当把这个老板的行为放到我们程序设计中来时,会发现这是一个普遍存在的现象。幸运的是,这种有病的现象在 OO (面向对象) 语言中可以避免了。下面就以 Java 语言为基础来引入我们本文的主题: 工厂模式。 二、分类 工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。 工厂模式在《Java与模式》中分为三类: 简单工厂模式 (Simple Factory) 工厂方法模式 (Factory Method) 抽象工厂模式 (Abstract Factory) 这三种模式从上到下逐步抽象,并且更具一般性。 GOF在《设计模式》一书中将工厂模式分为两类: 工厂方法模式 (Factory Method) 与抽象工厂模式 (Abstract Factory) 。将简单工厂模式 (Simple Factory) 看为工厂方法模式的一种特例,两者归为一类。 两者皆可,在本文使用《Java与模式》的分类方法。下面来看看这些工厂模式是怎么来"治病"的。 简单工厂模式, 简单工厂 (Simple Factory) 简单工厂模式又称静态工厂方法模式。从命名上就可以看出这个模式一定很简单。它存在的目的很简单: 定义一个用于创建对象的接口。 先来看看它的组成: 工厂类角色: 这是本模式的核心,含有一定的业务逻辑和判断逻辑。在 java 中它往往由一个具体类实现。 抽象产品角色: 它一般是具体产品继承的父类或者实现的接口。在 java 中由接口或者抽象类来实现。 具体产品角色: 工厂类所创建的对象就是此角色的实例。在 java 中由一个具体类实现。 来用类图来清晰的表示下的它们之间的关系 (如果对类图不太了解,请参考我关于类图的文章) : 那么简单工厂模式怎么来使用呢?我们就以简单工厂模式来改造老板坐车的方式——现在老板只需要坐在车里对司机说句: “开车"就可以了。 // 抽象产品 interface Car { void drive(); } // 具体产品 class Benze implements Car { public void drive() { System.out.println("Driving Benz"); } } // 具体产品 class Bmw implements Car { public void drive() { System.out.println("Driving Bmw"); } } // 工厂类 class Driver { // 工厂方法,注意返回类型为抽象产品 public static Car createCar(String s) throws Exception { // 判断逻辑,返回具体的产品角色给 Client if (s.equalsIgnoreCase("Benz")) { return new Benze(); } else if (s.equalsIgnoreCase("Bmw")) { return new Bmw(); } else { throw new Exception(); } } } // 客户端 public class Magnate { public static void main(String[] args) throws Exception { // 告诉司机我今天坐奔驰 Car car = Driver.createCar("benz"); // 下命令: 开车 car.drive(); car = Driver.createCar("bmw"); car.drive(); } } 将本程序空缺的其他信息填充完整后即可运行。如果你将所有的类放在一个文件中,请不要忘记只能有一个类被声明为public。本程序在jdk1.4 下运行通过。 ...

2026-04-16 · 3 min · 440 words · -

委托模式

委托模式 http://blog.sina.com.cn/s/blog_5f13e9910100g3ob.html http://www.iteye.com/topic/29541 委托模式是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。委托模式是一项基本技巧,许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式。委托模式使得我们可以用聚合来替代继承,它还使我们可以模拟mixin。 “委托"在C#中是一个语言级特性,而在Java语言中没有直接的对应,但是我们可以通过动态代理来实现委托!代码如下: Java代码 收藏代码 import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /* @author Liusheng 实现"委托"模式,用户需要实现InvocationHandler接口; 参考: http://www.uml.org.cn/j2ee/200411036.htm */ public abstract class Delegator implements InvocationHandler { //RelegateTo针对每个对象都要生成一个实例,因而非Static的log,代价比较高。 //protected Log _log = LogFactory.getLog(this.getClass()); //private static Log _log = LogFactory.getLog(RelegateTo.class); //————— protected Object obj_orgin = null; //原始对象 protected Object obj_proxy = null; //代理对象 //————— public Delegator() { //空 } public Delegator(Object orgin){ this.createProxy(orgin); } //————— protected Object createProxy(Object orgin) { obj_orgin = orgin; obj_proxy = Proxy.newProxyInstance( orgin.getClass().getClassLoader(), //加载器 ...

2014-08-08 · 2 min · 267 words · -

动态代理 Dynamic Proxy

动态代理 Dynamic Proxy 从JDK1.3开始,Java就引入了动态代理的概念。动态代理 (Dynamic Proxy) 可以帮助你减少代码行数,真正提高代码的可复用度。例如,你不必为所有的类的方法里面都写上相同的Log代码行,取而代之的是实用类的动态代理类。当然,这种便利是有条件的。本文简单介绍Java动态代理的原理,并实现一个被代理的Servlet创建,和调用的过程。 代理模式 (Proxy Pattern) 在JDK1.3以前,代理模式就已流行,所以得代理模式是生成一个和类相同接口的代理类,用户通过使用代理类来封装某个实现类。如图1,其目的是加强实现类的某个方法的功能,而不必改变原有的源代码。 <img src="http://p.blog.csdn.net/images/p_blog_csdn_net/tyrone1979/proxy1.JPG" alt="" /> 2.动态代理 (Dynamic Proxy) 随着Proxy的流行,Sun把它纳入到JDK1.3实现了Java的动态代理。动态代理和普通的代理模式的区别,就是动态代理中的代理类是由java.lang.reflect.Proxy类在运行期时根据接口定义,采用Java反射功能动态生成的。和java.lang.reflect.InvocationHandler结合,可以加强现有类的方法实现。如图2,图中的自定义Handler实现InvocationHandler接口,自定义Handler实例化时,将实现类传入自定义Handler对象。自定义Handler需要实现invoke方法,该方法可以使用Java反射调用实现类的实现的方法,同时当然可以实现其他功能,例如在调用实现类方法前后加入Log。而Proxy类根据Handler和需要代理的接口动态生成一个接口实现类的对象。当用户调用这个动态生成的实现类时,实际上是调用了自定义Handler的invoke方法。 <img src="http://p.blog.csdn.net/images/p_blog_csdn_net/tyrone1979/proxy2.JPG" alt="" width="683" height="284" /> 3.动态代理Servlet 虽然Web Application Server的产品很多,但Servlet的处理原理是相似的: 动态加载Servlet,调用Servlet的init方法 (只被调用一次) ,并保存到Servlet容器;Servlet使用时,调用Servlet的service方法。本文动态代理Servlet接口,使其init和service被调用时会在控制台打出方法调用前后信息。 首先实现2个Servlet,DefaultServlet和UserServlet ```java package org.colimas.servlet; import javax.servlet.Servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; public class DefaultServlet extends HttpServlet implements Servlet { public void init() throws ServletException { super.init(); System.out.println(DefaultServlet.class.getName()+":Running init"); } public String getServletInfo() { return DefaultServlet.class.getName(); } } package org.colimas.servlet; import java.io.IOException; import javax.servlet.Servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class UserServlet extends HttpServlet implements Servlet { private static final long serialVersionUID = -7016554795165038652L; public void init() throws ServletException { super.init(); System.out.println(UserServlet.class.getName()+":Running init"); } protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println(UserServlet.class.getName()+":Do UserSErvlet Get"); } public String getServletInfo() { return UserServlet.class.getName(); } } 然后实现InvocationHandler ...

2012-10-16 · 3 min · 547 words · -

单例 Singleton

单例 Singleton 静态内部类 static nested class 这种方法也是《Effective Java》上所推荐的。 这种写法仍然使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。 这是利用了JVM的特性:静态内部类时在类加载时实现的,因此不会受到多线程的影响,自然也就不会出现多个实例。 public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton() { } public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } } 单例模式 (Singleton) 介绍: 也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。 实现单例模式的思路是: 一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法 (必须是静态方法,通常使用getInstance这个名称); 当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。 注意事项: 单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。 解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁(虽然这样会降低效率)。 实现方式: 通常单例模式在Java语言中,有两种构建方式: 饿汉方式:指全局的单例实例在类装载时构建。(一般认为这种方式要更加安全些) ...

2012-10-16 · 2 min · 278 words · -

Builder, Factory

Builder, Factory http://www.cnblogs.com/shenfx318/archive/2007/01/28/632724.html 在设计模式的学习过程中,Builder与Factory是创建模式中两个经典的教程,给与了我们很多值得汲取的宝贵设计思想,然而Builder与Factory模式也是初学者容易混淆的两个模式,经常看到有人问及两者的区别与适用的场景,我在近一段设计模式的学习过程中同样碰到了这个问题,在两种模式的区别与联系间我看到的更多是后者,在这里愿意与大家分享一些我对Builder与Factory模式的感悟,有说的不对的地方,还请各位多加提点、指教。 写在前面 本文旨在两种模式间的对比与探讨,因此还希望各位看官首先对两个模式有一定的了解为好,因为常常看到有人提问说,Builder模式与抽象工厂 (Abstract Factory) 之间的区别,其实在我看来这两者间并无太多联系,因此也就谈不上区别,至于原因在此不做细述,有兴趣的朋友可以看看我写的有关 http://www.cnblogs.com/shenfx318/archive/2007/01/16/621237.html 抽象工厂的文章。故本文中所提的Factory模式皆指的是工厂方法 (Factory Method) 。 从Builder到Factory的演化 先来看看Builder模式,Builder模式的一般设计及实现 ```java public interface IBuilder { void BuildPart1(); void BuildPart2(); Product GetResult(); } //ConcreteBuilderA public class BuilderA : IBuilder { private Product product; public void BuildPart1() { product = new Product(); product.Add(“Part1 build by builderA”); } public void BuildPart2() { product.Add(“Part2 build by builderA”); } public Product GetResult() { return product; } } } //ConcreteBuilderB public class BuilderB : IBuilder //Director public class Director { public void Construct(IBuilder builder) { builder.BuildPart1(); builder.BuildPart2(); } } ...

2012-10-12 · 2 min · 378 words · -

持续改进,抽象工厂也反射

持续改进,抽象工厂也反射 http://www.cnblogs.com/shenfx318/archive/2007/01/16/621237.html 在我的上一篇文章(疑惑?改良?从简单工厂到工厂方法)中,详细论述了创建模式中简单工厂到工厂方法的演变过程,并试图结合工厂方法的设计以及.net中的反射机制之所长,改良出一种新型的工厂—反射工厂,这当然不是我的首创,经典的PetShop 中便有此工厂的身影。本文尝试按照前篇文章的思路,借着工厂方法到抽象工厂的演变过程而继续对抽象工厂进行改良,文章中的思想仅代表了作者当时的观点,有欠妥的地方,还请各位不吝赐教。 工厂模式 前面的文章提到了简单工厂和工厂方法其实是一码事,他们完成了将客户对产品功能的使用与创建具体产品职责的分割,不同的只不过是他们实现方式上的差异,工厂方法利用更加优雅的多态性取代了相对ugly的switch case…语句,从而很好的体现了设计原则中的OCP原则,此文章将不再强调这种实现上的差异性,而更多的强调两者之间设计思路上的共性,并将这种共性统称成为工厂模式,从而进一步与抽象工厂进行对比。 工厂的使用,选择的过程 工厂模式的使用,实际上是客户 (产品的消费者) 对于产品选择的过程,对于实现了相同功能的产品来讲,客户更加关心的是产品间的差异性,而工厂的作用则是将产品的生产过程封装,根据客户的要求直接返回客户需要的产品。注意,工厂只是隐藏了产品的生产过程,但是并没有剥夺客户选择的权利,那么客户的这个选择过程又是如何体现的呢?在简单工厂中,客户通过参数的形式告诉工厂需要什么样的产品,而在工厂方法中,客户通过对工厂的选择代替了直接对产品的选择,注意到工厂方法中一个工厂只有一个Create方法,也就是说一个工厂只负责生产一种产品,那么你选择了相应的工厂也就等同于你选择了对应的产品。就连改良后的反射工厂也没有消去对产品的选择,只不过是将这种选择外化 (外化到配置文件中,从而使得对代码的改动最小) 。可以说,正是由于产品间的差异性带给了客户选择的权利,这种权利是不应当被工厂取代的,那么工厂模式的价值又在哪里呢?答案是抽象与封装,工厂模式将由于客户的不同选择而可能导致的对已知事物的影响降到最低,途径是通过抽象产品取代具体产品,使得客户依赖于抽象 (越抽象的东西越稳定) ,同时将客户的选择封装到一处,隔离与具体产品间的依赖。 工厂模式与抽象工厂 前面说了这么多无关的,为得是做好铺垫,更加有益于对下文的理解,OK,终于该说说从工厂模式到抽象工厂的转变了,先来对比两张类图: 工厂方法(Factory Method) <img src="http://images.cnblogs.com/cnblogs_com/shenfx318/FactoryMethod3.jpg" alt="" width="506" height="186" border="0" /> 抽象工厂(Abstract Factory) 从图中我们能够看到哪些差异? 最明显的一点就是在工厂方法的类关系图中只有一类产品,他们实现了一个统一的接口,而抽象工厂有多个类别的产品,他们分别实现了不同的功能 (多个接口) 。其次的一点差别就是工厂本身所具有的方法数量不同,这点差异其实也是由第一点所导致的,工厂需要有生产不同类别产品的功能,如果抽象工厂中的产品的功能简化到一个,也便成为了工厂方法。 引出类别的概念,类别是指具有相同功能的一类产品的总称。 再来看选择的过程,在工厂方法中,客户关心的只不过是实现同一样功能的不同产品间的差异,这是一个一维选择的过程。 1 IFactory factory = new FactoryA(); //选择了工厂即选择了产品 2 IProduct productA = factory.Create(); //工厂只有一个Create方法,只能生产一种产品 而在抽象工厂中,客户首先要考虑的是需要哪一样功能,其次要考虑才是产品间的差异。也就是说这是一个二维选择的过程。 1 IFactory factory = new FactoryA(); //选择了某个具体工厂 2 IProduct productA = factory.CreateProductA(); //工厂具有多个Create方法,这里选择了其中的一个 ...

2012-10-12 · 1 min · 202 words · -

Factory, Abstract Factory, Factory Method, 和Builder

Factory, Abstract Factory, Factory Method, 和Builder 选这4个模式在一起讨论首先是因为它们的功能比较类似,都是用来制造对象实例的,从归类来说,他们都属于制造类模式(creational patterns),其次她们们在工作中比较常用,由于功能太过相似,往往导致在某个实际问题上让人举棋不定,似乎选哪个都可以解决问题,可是真的选择某个模式后,又会发现不是完全合适。今天我们就来讨论讨论在什么情况下选择什么模式来解决问题比较合适。 考虑到不是所有的朋友都对以上提到的4种模式都了如指掌,在开始讨论之前先简单的介绍一下这4个模式,对这4种模式熟悉的朋友也可以顺便回顾一下。请注意,这里我只会做简短的介绍,如果要比较深入地了解她们的话,还请去看模式相关的书籍,比较浅显易懂的我推荐 “Head First Design Patterns”。如果要在工作中反复参考的我推荐 “Applied Java Patterns”。那本经典的GOF Design Patterns由于写的时间比较早,举的例子不太适合现在的软件开发,我个人认为初学者或者是没有太多时间的朋友没有必要去读那本书,尽管她的确是经典。 Factory Pattern 中文叫工厂模式, 这个是我们在面向对象编程中最常用的模式之一了,她的主要功能就是制造对象,也正是这个原因才叫她为工厂模式,工厂干什么?生产产品。通常情况而言某个工厂所生产的产品总是一个系列的不同种类,大体上相同,细节上有差异。 图1 展示了一个非常简单的例子,通常CarFactory类会提供下列方法来生产车辆, 如代码片断1所示: public class CarFactory { public Car createBusinessCar() {….} public Car createSportCar() {…} } 代码片断 1 http://www.wiloon.com/wp-content/uploads/2012/10/x1pBG_wmiiVq4fKhMkEoTbjopPcKCE3u0uJjfqcYoYvgBeu5PDT5UndtcNdt48rY80PV_fd8Ce5pLpHavAj520cgRoVa4EmOhjE5VNS7GnBHl126UUuSSCrcM8YMFLVb3mp_UsivKRLJ1HKESriM5FDQA-1.gif 图1 Factory Pattern Abstract Factory Pattern 中文叫抽象工厂模式,顾名思义,就是把工厂模式在进一步抽象化,进一步细化。我们继续沿用上面的例子,不过这次增加产品的种类,如图2所示。由于我们增加了一层分类,当我们要生产某种车的时候就需要询问要哪种车, 商用还是跑车?不然的话就要增加create方法,如果分类多了,create方法就会成几何数量增长。 public class CarFactory { public Car createBusinessBMWCar() {….} public Car createBusinessBenzCar() {…} public Car createSportBMWCar() {….} public Car createSportBenzCar() {…} } 代码片断 2 图2 bad Factory Pattern ...

2012-10-11 · 2 min · 236 words · -

抽象工厂模式解析例子

抽象工厂模式解析例子 抽象工厂模式中的有以下的四种角色: 抽象工厂 (Abstract Factory) 角色: 担任这个角色的是工厂方法模式的核心,它是与应用系统业务逻辑无关的。 具体工厂 (Concrete Factory) 角色: 这个角色直接在客户端的调用下创建产品的实例。这个角色含有选择合适的产品对象的逻辑,而这个逻辑是与应用系统的业务逻辑紧密相关的。 抽象产品 (Abstract Product) 角色: 担任这个角色的类是工厂方法模式所创建的对象的父类,或它们共同拥有的接口。 具体产品 (Concrete Product) 角色: 这个角色用以代表具体的产品。 抽象工厂模式就相当于创建实例对象的new,由于经常要根据类生成实例对象,抽象工厂模式也是用来创建实例对象的,所以在需要新的事例对象时便可以考虑是否使用工厂模式。虽然这样做可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。 举例来说: 生产餐具和相应食物的工厂,有两个车间,其中一个车间用以生产餐具,一个车间用以生产相应的食物。 当消费者消费时,只需要向相应的具体工厂请求具体餐具和具体食物便可以使用餐具消费食物。 每个具体工厂生产出来的具体产品根据不同工厂的不同各不相同,但是客户使用产品的方法是一致的。比如客户在得到餐具和食物之后,两者的搭配是正确的 (使用汤匙喝牛奶,使用刀子切面包) 。 在本例子中有3个具体工厂AKetchen, BKetchen, BKetchen,分别生产牛奶和汤匙、面包和刀、肉和叉子。牛奶、面包和肉都实现了食物接口。汤匙、刀和叉子都实现了餐具接口。 抽象工厂的接口定义如下所示; public interface KetchenFactory{ public Food getFood(); public TableWare getTableWare(); } //抽象餐具的接口定义如下所示: public interface TableWare{ public String getTool(); } //抽象食物的接口定义如下所示: public interface Food{ public String getEatable(); } //而具体的实现也非常简单,以AKetchen为例子 //具体工厂AKetchen的定义如下所示; public class AKetchen implements KetchenFactory{ public Food getFood(){ return new Milk(); } public TableWare getTableWare(){ return new Spoon(); } } //具体餐具(spoon)的定义如下所示: public class Spoon implements TableWare{ public String getTool() { return "spoon"; } } //具体食物(milk)的定义如下所示: public class Milk implements Food{ public String getEatable(){ return "milk"; } } //客户端的定义如下: public class Client{ public void eat(KetchenFactory k){ System.out.println("A person eat "+k.getFood().getEatable() +" with "+k.getTableWare().getTool()+"!"); } public static void main(String[] args){ Client client= new Client(); KetchenFactory kf = new AKetchen(); client.eat(kf); kf=new BKetchen(); client.eat(kf); kf= new CKetchen(); client.eat(kf); } } 小结: 抽象工厂模式特别适合于这样的一种产品结构: 产品分为几个系列,在每个系列中,产品的布局都是要同的,在一个系列中某个位置的产品,在另一个系列中一定有一个对应的产品。这样的产品结构是存在的,这几个系列中同一位置的产品可能是互斥的,它们是针对不同客户的解决方案,每个客户都只择其一。 ...

2012-10-09 · 1 min · 159 words · -

观察者模式, Observer pattern

观察者模式, Observer pattern 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 观察者模式 (Observer)又称发布-订阅模式 (Publish-Subscribe:Pub/Sub)。它是一种通知机制,让发送通知的一方 (被观察方)和接收通知的一方 (观察者)能彼此分离,互不影响。 观察者模式 (有时又被称为发布-订阅模式、模型-视图模式、源-收听者模式或从属者模式) 是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过调用各观察者所提供的方法来实现。此种模式通常被用来实作事件处理系统。 此模式完美的将观察者和被观察的对象分离开。举个例子,用户界面可以作为一个观察者,业务数据是被观察者,用户界面观察业务数据的变化,发现数据变化后,就显示在界面上。面向对象设计的一个原则是: 系统中的每个类将重点放在某一个功能上,而不是其他方面。一个对象只做一件事情。观察者模式在模块之间划定了清晰的界限,提高了应用程序的可维护性和重用性。 观察者模式有很多实现方式,从根本上说,该模式必须包含两个角色: 观察者和被观察对象。在刚才的例子中,业务数据是被观察对象,用户界面是观察者。观察者和被观察者之间存在"观察"的逻辑关联,当被观察者发生改变的时候,观察者就会观察到这样的变化,并且做出相应的响应。如果在用户界面、业务数据之间使用这样的观察过程,可以确保界面和数据之间划清界限,假定应用程序的需求发生变化,需要修改界面的表现,只需要重新构建一个用户界面,业务数据不需要发生变化。 实现观察者模式的时候要注意,观察者和被观察对象之间的互动关系不能体现成类之间的直接调用,否则就将使观察者和被观察对象之间紧密的耦合起来,从根本上违反面向对象的设计的原则。无论是观察者"观察"观察对象,还是被观察者将自己的改变"通知"观察者,都不应该直接调用。 实现观察者模式有很多形式,比较直观的一种是使用一种"注册——通知——撤销注册"的形式。 @startuml class Publisher interface Subscriber Publisher o-right- Subscriber class ConcreateSubscriber Subscriber <|-- ConcreateSubscriber class Client ConcreateSubscriber <.. Client Publisher<--Client @enduml @startuml class Demo class Editor note right: 发布者\nPublisher\n被观察对象\nSubject Editor<-- Demo interface EventListener note left: 订阅者\nSubscriber\n观察者\nObserver class EmailNotificationListener note left: 具体订阅者 EventListener<|-- EmailNotificationListener EmailNotificationListener<.. Demo class EventManager EventManager --o Editor EventListener -right-o EventManager class LogOpenListener EventListener<|-- LogOpenListener LogOpenListener<.. Demo @enduml 发布者, Publisher, 被观察对象, Subject 发布者 (Publisher) 会向其他对象发送值得关注的事件。 事件会在发布者自身状态改变或执行特定行为后发生。 发布者中包含一个允许新订阅者加入和当前订阅者离开列表的订阅构架。 ...

2011-11-20 · 3 min · 620 words · -

工厂模式

工厂模式 工厂模式是我们最常用的模式了,著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见。 为什么工厂模式是如此常用?因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑使用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。 我们以类Sample为例, 如果我们要创建Sample的实例对象: Sample sample=new Sample(); 可是,实际情况是,通常我们都要在创建sample实例时做点初始化的工作,比如赋值 查询数据库等。 首先,我们想到的是,可以使用Sample的构造函数,这样生成实例就写成: Sample sample=new Sample(参数); 但是,如果创建sample实例时所做的初始化工作不是象赋值这样简单的事,可能是很长一段代码,如果也写入构造函数中,那你的代码很难看了 (就需要Refactor重构) 。 为什么说代码很难看,初学者可能没有这种感觉,我们分析如下,初始化工作如果是很长一段代码,说明要做的工作很多,将很多工作装入一个方法中,相当于将很多鸡蛋放在一个篮子里,是很危险的,这也是有背于Java面向对象的原则,面向对象的封装(Encapsulation)和分派(Delegation)告诉我们,尽量将长的代码分派"切割"成每段,将每段再"封装"起来(减少段和段之间偶合联系性),这样,就会将风险分散,以后如果需要修改,只要更改每段,不会再发生牵一动百的事情。 在本例中,首先,我们需要将创建实例的工作与使用实例的工作分开, 也就是说,让创建实例所需要的大量初始化工作从Sample的构造函数中分离出去。 这时我们就需要Factory工厂模式来生成对象了,不能再用上面简单new Sample(参数)。还有,如果Sample有个继承如MySample, 按照面向接口编程,我们需要将Sample抽象成一个接口.现在Sample是接口,有两个子类MySample 和HisSample .我们要实例化他们时,如下: Sample mysample=new MySample(); Sample hissample=new HisSample(); 随着项目的深入,Sample可能还会"生出很多儿子出来", 那么我们要对这些儿子一个个实例化,更糟糕的是,可能还要对以前的代码进行修改:加入后来生出儿子的实例.这在传统程序中是无法避免的. 但如果你一开始就有意识使用了工厂模式,这些麻烦就没有了. 工厂方法 你会建立一个专门生产Sample实例的工厂: public class Factory{ public static Sample creator(int which){ //getClass 产生Sample 一般可使用动态类装载装入类。 if (which==1) return new SampleA(); else if (which==2) return new SampleB(); } } 那么在你的程序中,如果要实例化Sample时.就使用 ...

2011-10-29 · 2 min · 348 words · -