行为型模式-命令模式

维基百科 命令模式 在面向对象编程的范畴中,命令模式(英语:Command pattern)是一种设计模式,它尝试以对象来代表实际行动。命令对象可以把行动(action) 及其参数封装起来,于是这些行动可以被: 重复多次 取消(如果该对象有实现的话) 取消后又再重做 这些都是现代大型应用程序所必须的功能,即“撤销”及“重复”。除此之外,可以用命令模式来实现的功能例子还有: 交易行为 进度列 向导 用户界面按钮及功能表项目 线程 pool 宏收录 Java import java.util.List; import java.util.ArrayList; /* The Command interface */ public interface Command { void execute(); } /* The Invoker class */ public class Switch { private List<Command> history = new ArrayList<Command>(); public Switch() { } public void storeAndExecute(Command cmd) { this.history.add(cmd); // optional cmd.execute(); } } /* The Receiver class */ public class Light { public Light() { } public void turnOn() { System.out.println("The light is on"); } public void turnOff() { System.out.println("The light is off"); } } /* The Command for turning on the light - ConcreteCommand #1 */ public class FlipUpCommand implements Command { private Light theLight; public FlipUpCommand(Light light) { this.theLight = light; } public void execute(){ theLight.turnOn(); } } /* The Command for turning off the light - ConcreteCommand #2 */ public class FlipDownCommand implements Command { private Light theLight; public FlipDownCommand(Light light) { this.theLight = light; } public void execute() { theLight.turnOff(); } } /* The test class or client */ public class PressSwitch { public static void main(String[] args){ Light lamp = new Light(); Command switchUp = new FlipUpCommand(lamp); Command switchDown = new FlipDownCommand(lamp); Switch mySwitch = new Switch(); try { if ("ON".equalsIgnoreCase(args[0])) { mySwitch.storeAndExecute(switchUp); } else if ("OFF".equalsIgnoreCase(args[0])) { mySwitch.storeAndExecute(switchDown); } else { System.out.println("Argument \"ON\" or \"OFF\" is required."); } } catch (Exception e) { System.out.println("Arguments required."); } } } 例子 Receiver 知道如何实现与执行一个请求相关的操作。任何类都可能作为一个接收者。 ...

August 26, 2019 · 2 min · zhangxiaofeng05

行为型模式-责任链模式

维基百科 责任链模式 责任链模式在面向对象程式设计里是一种软件设计模式,它包含了一些命令对象和一系列的处理对象。每一个处理对象决定它能处理哪些命令对象,它也知道如何将它不能处理的命令对象传递给该链中的下一个处理对象。该模式还描述了往该处理链的末尾添加新的处理对象的方法。 Java 以下的日志类(logging)例子演示了该模式。 每一个logging handler首先决定是否需要在该层做处理,然后将控制传递到下一个logging handler。程序的输出是: Writing to debug output: Entering function y. Writing to debug output: Step1 completed. Sending via e-mail: Step1 completed. Writing to debug output: An error has occurred. Sending via e-mail: An error has occurred. Writing to stderr: An error has occurred. 注意:该例子不是日志类的推荐实现方式。 同时,需要注意的是,通常在责任链模式的实现中,如果在某一层已经处理了这个logger,那么这个logger就不会传递下去。在我们这个例子中,消息会一直传递到最底层不管它是否已经被处理。 abstract class Logger { public static int ERR = 3; public static int NOTICE = 5; public static int DEBUG = 7; protected int mask; // The next element in the chain of responsibility protected Logger next; public Logger setNext( Logger l) { next = l; return this; } public final void message( String msg, int priority ) { if ( priority <= mask ) { writeMessage( msg ); if ( next != null ) { next.message( msg, priority ); } } } protected abstract void writeMessage( String msg ); } class StdoutLogger extends Logger { public StdoutLogger( int mask ) { this.mask = mask; } protected void writeMessage( String msg ) { System.out.println( "Writting to stdout: " + msg ); } } class EmailLogger extends Logger { public EmailLogger( int mask ) { this.mask = mask; } protected void writeMessage( String msg ) { System.out.println( "Sending via email: " + msg ); } } class StderrLogger extends Logger { public StderrLogger( int mask ) { this.mask = mask; } protected void writeMessage( String msg ) { System.out.println( "Sending to stderr: " + msg ); } } public class ChainOfResponsibilityExample { public static void main( String[] args ) { // Build the chain of responsibility Logger l = new StdoutLogger( Logger.DEBUG).setNext( new EmailLogger( Logger.NOTICE ).setNext( new StderrLogger( Logger.ERR ) ) ); // Handled by StdoutLogger l.message( "Entering function y.", Logger.DEBUG ); // Handled by StdoutLogger and EmailLogger l.message( "Step1 completed.", Logger.NOTICE ); // Handled by all three loggers l.message( "An error has occurred.", Logger.ERR ); } } 例子 public interface Request { } public class DimissionRequest implements Request { } public class AddMoneyRequest implements Request { } public class LeaveRequest implements Request { } Handler ...

August 26, 2019 · 3 min · zhangxiaofeng05

结构型模式-代理模式

维基百科 代理模式 代理模式(英语:Proxy Pattern)是程序设计中的一种设计模式。 所谓的代理者是指一个类别可以作为其它东西的接口。代理者可以作任何东西的接口:网络连接、存储器中的大对象、文件或其它昂贵或无法复制的资源。 著名的代理模式例子为引用计数(英语:reference counting)指针对象。 当一个复杂对象的多份副本须存在时,代理模式可以结合享元模式以减少存储器用量。典型作法是创建一个复杂对象及多个代理者,每个代理者会引用到原本的复杂对象。而作用在代理者的运算会转送到原本对象。一旦所有的代理者都不存在时,复杂对象会被移除。 Java 以下Java示例解释"虚拟代理"模式。ProxyImage 类别用来访问远程方法。 import java.util.*; interface Image { public void displayImage(); } //on System A class RealImage implements Image { private String filename; public RealImage(String filename) { this.filename = filename; loadImageFromDisk(); } private void loadImageFromDisk() { System.out.println("Loading " + filename); } public void displayImage() { System.out.println("Displaying " + filename); } } //on System B class ProxyImage implements Image { private String filename; private Image image; public ProxyImage(String filename) { this.filename = filename; } public void displayImage() { if(image == null) image = new RealImage(filename); image.displayImage(); } } class ProxyExample { public static void main(String[] args) { Image image1 = new ProxyImage("HiRes_10MB_Photo1"); Image image2 = new ProxyImage("HiRes_10MB_Photo2"); image1.displayImage(); // loading necessary image2.displayImage(); // loading necessary } } 程序的输出为: ...

August 25, 2019 · 1 min · zhangxiaofeng05

结构型模式-享元模式

维基百科 享元模式 享元模式(英语:Flyweight Pattern)是一种软件设计模式。它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于当大量物件只是重复因而导致无法令人接受的使用大量内存。通常物件中的部分状态是可以分享。常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元。 典型的享元模式的例子为文书处理器中以图形结构来表示字符。一个做法是,每个字形有其字型外观, 字模 metrics, 和其它格式资讯,但这会使每个字符就耗用上千字节。取而代之的是,每个字符参照到一个共享字形物件,此物件会被其它有共同特质的字符所分享;只有每个字符(文件中或页面中)的位置才需要另外储存。 Java 以下程式用来解释上述的文字。这个例子用来解释享元模式利用只加载执行任务时所必需的最少资料,因而减少内存使用量。 public enum FontEffect { BOLD, ITALIC, SUPERSCRIPT, SUBSCRIPT, STRIKETHROUGH } public final class FontData { /** * A weak hash map will drop unused references to FontData. * Values have to be wrapped in WeakReferences, * because value objects in weak hash map are held by strong references. */ private static final WeakHashMap<FontData, WeakReference<FontData>> FLY_WEIGHT_DATA = new WeakHashMap<FontData, WeakReference<FontData>>(); private final int pointSize; private final String fontFace; private final Color color; private final Set<FontEffect> effects; private FontData(int pointSize, String fontFace, Color color, EnumSet<FontEffect> effects) { this.pointSize = pointSize; this.fontFace = fontFace; this.color = color; this.effects = Collections.unmodifiableSet(effects); } public static FontData create(int pointSize, String fontFace, Color color, FontEffect... effects) { EnumSet<FontEffect> effectsSet = EnumSet.noneOf(FontEffect.class); for (FontEffect fontEffect : effects) { effectsSet.add(fontEffect); } // We are unconcerned with object creation cost, we are reducing overall memory consumption FontData data = new FontData(pointSize, fontFace, color, effectsSet); // Retrieve previously created instance with the given values if it (still) exists WeakReference<FontData> ref = FLY_WEIGHT_DATA.get(data); FontData result = (ref != null) ? ref.get() : null; // Store new font data instance if no matching instance exists if (result == null) { FLY_WEIGHT_DATA.put(data, new WeakReference<FontData> (data)); result = data; } // return the single immutable copy with the given values return result; } @Override public boolean equals(Object obj) { if (obj instanceof FontData) { if (obj == this) { return true; } FontData other = (FontData) obj; return other.pointSize == pointSize && other.fontFace.equals(fontFace) && other.color.equals(color) && other.effects.equals(effects); } return false; } @Override public int hashCode() { return (pointSize * 37 + effects.hashCode() * 13) * fontFace.hashCode(); } // Getters for the font data, but no setters. FontData is immutable. } 例子 Flyweight ...

August 25, 2019 · 2 min · zhangxiaofeng05

结构型模式-外观模式

维基百科 外观模式 外观模式(Facade pattern),是软件工程中常用的一种软件设计模式,它为子系统中的一组接口提供一个统一的高层接口,使得子系统更容易使用。 Java 这是一个抽象的示例。一个客户“you”通过外观接口“computer”获取计算机内部复杂的系统信息。 /* Complex parts */ class CPU { public void freeze() { ... } public void jump(long position) { ... } public void execute() { ... } } class Memory { public void load(long position, byte[] data) { ... } } class HardDrive { public byte[] read(long lba, int size) { ... } } /* Façade */ class Computer { public void startComputer() { cpu.freeze(); memory.load(BOOT_ADDRESS, hardDrive.read(BOOT_SECTOR, SECTOR_SIZE)); cpu.jump(BOOT_ADDRESS); cpu.execute(); } } /* Client */ class You { public static void main(String[] args) { Computer facade = new Computer(); facade.startComputer(); } } 例子 抽象类 ...

August 25, 2019 · 2 min · zhangxiaofeng05

结构型模式-装饰模式

维基百科 修饰模式 修饰模式,是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。就功能而言,修饰模式相比生成子类更为灵活,这样可以给某个对象而不是整个类添加一些功能。 介绍 通过使用修饰模式,可以在运行时扩充一个类的功能。原理是:增加一个修饰类包裹原来的类,包裹的方式一般是通过在将原来的对象作为修饰类的构造函数的参数。装饰类实现新的功能,但是,在不需要用到新功能的地方,它可以直接调用原来的类中的方法。修饰类必须和原来的类有相同的接口。 修饰模式是类继承的另外一种选择。类继承在编译时候增加行为,而装饰模式是在运行时增加行为。 当有几个相互独立的功能需要扩充时,这个区别就变得很重要。在有些面向对象的编程语言中,类不能在运行时被创建,通常在设计的时候也不能预测到有哪几种功能组合。这就意味着要为每一种组合创建一个新类。相反,修饰模式是面向运行时候的对象实例的,这样就可以在运行时根据需要进行组合。一个修饰模式的示例是JAVA里的Java I/O Streams的实现。 动机 例如,一个窗口系统中的窗口,允许这个窗口内容滚动,我们希望给它添加水平或垂直滚动条。假设窗口通过“Window”类实例来表示,并且假设它没有添加滚动条功能。我们可以创建一个子类“ScrollingWindow”来提供,或者我们可以创建一个ScrollingWindowDecorator来为已存在的Window对象添加这个功能。在这点上,只要是解决方案就可以了。 现在我们假设希望选择给我们的窗口添加边框,同样,我们的原始Window类不支持。ScrollingWindow子类现在会造成一个问题,因为它会有效的创建一种新的窗口。如果我们想要给所有窗口添加边框,我们必须创建WindowWithBorder和ScrollingWindowWithBorder子类。显然,这个问题由于被添加类而变得更糟了。对于修饰模式,我们简单的创建一个新类BorderedWindowDecorator,在运行时,我们能够使用ScrollingWindowDecorator或BorderedWindowDecorator或两者结合来修饰已存在的窗口。 一个修饰能够被应用的另一个好例子是当有需要根据某套规则或者几个平行的规则集(不同的用户凭据等)限制访问对象的属性或方法时。 一个对象的属性或方法按照某组规则或几个并行规则(不同用户证书等)需要限制访问时,在这种情况下,不是在原始对象中实现访问控制而是在他的使用中不变或不知道任何限制,并且他被包装在一个访问控制修饰对象中,这个对象能够对允许的原始对象的接口子集服务。 应用 Java IO 流为典型的装饰模式。 Java 这个JAVA示例使用window/scrolling情境。 // The Window interface class public interface Window { public void draw(); // Draws the Window public String getDescription(); // Returns a description of the Window } // implementation of a simple Window without any scrollbars public class SimpleWindow implements Window { public void draw() { // Draw window } public String getDescription() { return "simple window"; } } 以下类包含所有Window类的decorator,以及修饰类本身。 ...

August 25, 2019 · 2 min · zhangxiaofeng05

结构型模式-桥接模式

维基百科 桥接模式 桥接模式是软件设计模式中最复杂的模式之一,它把事物对象和其具体行为、具体特征分离开来,使它们可以各自独立的变化。事物对象仅是一个抽象的概念。如“圆形”、“三角形”归于抽象的“形状”之下,而“画圆”、“画三角”归于实现行为的“画图”类之下,然后由“形状”调用“画图”。 Java /** "Implementor" */ interface DrawingAPI { public void drawCircle(double x, double y, double radius); } /** "ConcreteImplementor" 1/2 */ class DrawingAPI1 implements DrawingAPI { public void drawCircle(double x, double y, double radius) { System.out.printf("API1.circle at %f:%f radius %f\n", x, y, radius); } } /** "ConcreteImplementor" 2/2 */ class DrawingAPI2 implements DrawingAPI { public void drawCircle(double x, double y, double radius) { System.out.printf("API2.circle at %f:%f radius %f\n", x, y, radius); } } /** "Abstraction" */ interface Shape { public void draw(); // low-level public void resizeByPercentage(double pct); // high-level } /** "Refined Abstraction" */ class CircleShape implements Shape { private double x, y, radius; private DrawingAPI drawingAPI; public CircleShape(double x, double y, double radius, DrawingAPI drawingAPI) { this.x = x; this.y = y; this.radius = radius; this.drawingAPI = drawingAPI; } // low-level i.e. Implementation specific public void draw() { drawingAPI.drawCircle(x, y, radius); } // high-level i.e. Abstraction specific public void resizeByPercentage(double pct) { radius *= pct; } } /** "Client" */ class BridgePattern { public static void main(String[] args) { Shape[] shapes = new Shape[2]; shapes[0] = new CircleShape(1, 2, 3, new DrawingAPI1()); shapes[1] = new CircleShape(5, 7, 11, new DrawingAPI2()); for (Shape shape : shapes) { shape.resizeByPercentage(2.5); shape.draw(); } } } 运行结果: ...

August 24, 2019 · 2 min · zhangxiaofeng05

结构型模式-适配器模式

维基百科 适配器模式 在设计模式中,适配器模式(英语:adapter pattern)有时候也称包装样式或者包装(wrapper)。将一个类的接口转接成用户所期待的。一个适配使得因接口不兼容而不能在一起工作的类能在一起工作,做法是将类自己的接口包裹在一个已存在的类中。 结构 有两种类型的适配器模式: 对象适配器模式 在这种适配器模式中,适配器容纳一个它包裹的类的实例。在这种情况下,适配器调用被包裹对象的物理实体。 类适配器模式 这种适配器模式下,适配器继承自已实现的类(一般多重继承)。 例子 Target 定义Client使用的与特定领域相关的接口。 public interface Target { void adapteeMethod(); void adapterMethod(); } Adaptee 定义一个已经存在的接口,这个接口需要适配。 public class Adaptee { public void adapteeMethod(){ System.out.println("Adaptee method"); } } Adapter 对Adaptee的接口与Target接口进行适配 public class Adapter implements Target{ private Adaptee adaptee; public Adapter(Adaptee adaptee){ this.adaptee=adaptee; } @Override public void adapteeMethod() { adaptee.adapteeMethod(); } @Override public void adapterMethod() { System.out.println("Adapter method"); } } Client 与符合Target接口的对象协同。 public class Test { public static void main(String[] args) { Target target = new Adapter(new Adaptee()); target.adapteeMethod(); target.adapterMethod(); } } 运行结果: ...

August 24, 2019 · 1 min · zhangxiaofeng05

创建型模式-原型模式

维基百科 原型模式 原型模式是创建型模式的一种,其特点在于通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的“原型”,这个原型是可定制的。 原型模式多用于创建复杂的或者耗时的实例,因为这种情况下,复制一个已经存在的实例使程序运行更高效;或者创建值相等,只是命名不一样的同类数据。 Java /** Prototype Class **/ public class Cookie implements Cloneable { public Object clone() throws CloneNotSupportedException { //In an actual implementation of this pattern you would now attach references to //the expensive to produce parts from the copies that are held inside the prototype. return (Cookie) super.clone(); } } /** Concrete Prototypes to clone **/ public class CoconutCookie extends Cookie { } /** Client Class**/ public class CookieMachine { private Cookie cookie;//cookie必须是可复制的 public CookieMachine(Cookie cookie) { this.cookie = cookie; } public Cookie makeCookie() { try { return (Cookie) cookie.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } public static void main(String args[]){ Cookie tempCookie = null; Cookie prot = new CoconutCookie(); CookieMachine cm = new CookieMachine(prot); //设置原型 for(int i=0; i<100; i++) tempCookie = cm.makeCookie();//通过复制原型返回多个cookie } } 例子 Prototype 声明一个克隆自身的接口 ...

August 24, 2019 · 2 min · zhangxiaofeng05

创建型模式-单例模式

维基百科 单例模式 单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。 实现单例模式的思路是:一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。 单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。 解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁(虽然这样会降低效率)。 构建方式 通常单例模式在Java语言中,有两种构建方式: 懒汉方式。指全局的单例实例在第一次被使用时构建。 饿汉方式。指全局的单例实例在类装载时构建。 Java 在Java语言中,单例模式(饿汉模式)应用的例子如下述代码所示: public class Singleton { private static final Singleton INSTANCE = new Singleton(); // Private constructor suppresses // default public constructor private Singleton() {}; public static Singleton getInstance() { return INSTANCE; } } 在Java编程语言中,单例模式(懒汉模式)应用的例子如下述代码所示 (此种方法只能用在JDK5及以后版本(注意 INSTANCE 被声明为 volatile),之前的版本使用“双重检查锁”会发生非预期行为): public class Singleton { private static volatile Singleton INSTANCE = null; // Private constructor suppresses // default public constructor private Singleton() {}; //Thread safe and performance promote public static Singleton getInstance() { if(INSTANCE == null){ synchronized(Singleton.class){ // When more than two threads run into the first null check same time, // to avoid instanced more than one time, it needs to be checked again. if(INSTANCE == null){ INSTANCE = new Singleton(); } } } return INSTANCE; } } 例子 Singleton ...

August 23, 2019 · 1 min · zhangxiaofeng05