组合模式看起来就像对象组的树形结构,一个对象里面包含一个或一组其余的对象。它是属于结构型模式。
例如,一个公司包括不少个部门,每一个部门又包括不少人,这个用数据结构来表示就是树形结构,实际上也是用到来组合模式,多我的组成一个部门,多个部门组成一个公司。算法
例如,咱们用下面这个公司、部门、员工的例子来更好的理解组合模式。设计模式
class Company { private String name; private List<Dept> depts; } class Dept { private String name; private List<User> users; } class User { private String name; }
装饰器设计模式容许咱们动态地向对象添加功能和行为,而不会影响同一类中其余现有对象的行为。而且能够根据咱们的要求和选择将此自定义功能应用于单个对象。
假如使用继承来扩展类的行为,这发生在编译期,该类的全部实例都得到扩展行为。安全
装饰器设计模式的特色:
它容许咱们在运行时向对象(而不是类)添加功能。
它是一种结构模式,它为现有类提供了一个包装器。
它使用抽象类或接口与组合来实现包装器。
它建立装饰器类,它包装原始类并经过保持类方法的签名不变来提供其余功能。
它最经常使用于应用单一责任原则,由于咱们将功能划分为具备独特关注区域的类。微信
例如,咱们用下面这个画图形的例子来更好的理解装饰模式。数据结构
//定义一个形状的接口 public interface Shape { void draw(); void resize(); } //一个画圆的实现 public class Circle implements Shape { @Override public void draw() { System.out.println("Drawing Circle"); } @Override public void resize() { System.out.println("Resizing Circle"); } } //一个画矩形的实现 public class Rectangle implements Shape { @Override public void draw() { System.out.println("Drawing Rectangle"); } @Override public void resize() { System.out.println("Resizing Rectangle"); } } //定义一个形状的装饰器抽象类,并用组合模式定义一个形状的属性 public abstract class ShapeDecorator implements Shape { protected Shape decoratedShape; public ShapeDecorator(Shape decoratedShape) { super(); this.decoratedShape = decoratedShape; } } //颜色的枚举 public enum Color { RED, GREEN, BLUE } //线条样式的枚举 public enum LineStyle { SOLID, DASH, DOT } //定义一个填充颜色的实现类实现装饰器,并重写 draw() 方法,resize() 方法咱们能够保持不变,也能够自定义,看使用场景 public class FillColorDecorator extends ShapeDecorator { protected Color color; public FillColorDecorator(Shape decoratedShape, Color color) { super(decoratedShape); this.color = color; } @Override public void draw() { decoratedShape.draw(); System.out.println("Fill Color: " + color); } @Override public void resize() { decoratedShape.resize(); } } //定义一个线条样式的实现类实现装饰器,并重写 draw() 方法,resize() 方法咱们能够保持不变,也能够自定义,看使用场景 public class LineStyleDecorator extends ShapeDecorator { protected LineStyle style; public LineStyleDecorator(Shape decoratedShape, LineStyle style) { super(decoratedShape); this.style = style; } @Override public void draw() { decoratedShape.draw(); System.out.println("Line Style: " + style); } // @Override public void resize() { decoratedShape.resize(); } } //使用装饰器模式 public class Client { public static void main(String[] args) { //在使用时能够任意组装,提高代码灵活性和扩展性。 Shape circle = new FillColorDecorator(new LineStyleDecorator(new Circle(), LineStyle.DASH), Color.RED); circle.draw(); } }
它提供了一个能够访问系统的接口,这个接口里面的实现可能很复杂,调用了其余多个接口,咱们并不知道它里面的具体实现,隐藏了系统的复杂性。它属于结构型模式。ide
它的优势:
一、减小系统相互依赖。
二、提升灵活性。
三、提升了安全性。 性能
它的缺点:
不符合开闭原则,若是要改东西很麻烦,继承重写都不合适。网站
以旅行社网站为例,它能够预订酒店和航班,咱们来更好的理解下这个模式。this
//提供酒店相关的接口 public class HotelBooker{ public ArrayList<Hotel> getHotelNamesFor(Date from, Date to) { //返回该时间段有效的酒店 } } //提供航班相关的接口 public class FlightBooker{ public ArrayList<Flight> getFlightsFor(Date from, Date to) { //返回该时间段有效的航班 } } //提供一个旅行对外的接口,一次返回酒店和航班信息 public class TravelFacade{ private HotelBooker hotelBooker; private FlightBooker flightBooker; public void getFlightsAndHotels(Date from, Data to) { ArrayList<Flight> flights = flightBooker.getFlightsFor(from, to); ArrayList<Hotel> hotels = hotelBooker.getHotelsFor(from, to); } } //调用旅行外观模式 public class Client{ public static void main(String[] args) { TravelFacade facade = new TravelFacade(); facade.getFlightsAndHotels(from, to); } }
享元模式主要用于减小建立对象的数量,以减小内存占用和提升性能。使许多细粒度对象的重用,使得大量对象的利用更加有效。它属于结构型模式。url
它的优势:
大大减小对象的建立,下降系统的内存,使效率提升。
它的缺点:
由于一个对象你们共享,最好就不要有状态区分,假若有状态的话,
须要分离出外部状态和内部状态,并且外部状态具备固有化的性质,不该该随着内部状态的变化而变化,不然会形成系统的混乱。
例如,咱们用一个画线条的例子来更好的理解这个模式。
//定义一个画线条的接口 public interface LineFlyweight{ public Color getColor(); public void draw(Point location); } //定义一个画线条的实现 public class Line implements LineFlyweight{ private Color color; public Line(Color c){ color = c; } public Color getColor(){ return color; } public void draw(Point location){ //实现 } } //定一个画线条的工厂类,根据颜色来获取线条,而且把不一样颜色的线存储在一个 pool 中,方便下次直接使用 public class LineFlyweightFactory{ private List<LineFlyweight> pool; public LineFlyweightFactory(){ pool = new ArrayList<LineFlyweight>(); } public LineFlyweight getLine(Color c){ //循环检查这个颜色的线是否存在,存在直接返回 for(LineFlyweight line: pool){ if(line.getColor().equals(c)){ return line; } } //假如不存在,就建立一个放入这个 pool 中,方便下次直接使用 LineFlyweight line = new Line(c); pool.add(line); return line; } } //调用享元模式 public class Client{ public static void main(String[] args) { //调用 LineFlyweightFactory factory = new LineFlyweightFactory(); LineFlyweight line = factory.getLine(Color.RED); LineFlyweight line2 = factory.getLine(Color.RED); line.draw(new Point(100, 100)); line2.draw(new Point(200, 100)); } }
它经过一个代理类对外提供访问,代理类在真正调用实现类。对外部来讲,并不知道真正实现类的详情,提升类系统的安全性。咱们能够在代理类里作一系列拦截,把异常的请求都提早处理掉,扩展性很高。它属于结构型模式。
它的优势:
一、职责清晰。
二、高扩展性。
三、更安全。
它的缺点:
一、因为在客户端和真实主题之间增长了代理对象,所以可能会形成请求的处理速度变慢。
二、实现代理模式须要额外的工做,有些代理模式的实现很是复杂。
例如,咱们用下面这个代理显示图片的例子来更好的理解代理模式。
//定义的图片接口 public interface Image{ public void displayImage(); } //真正的实现类 public class RealImage implements Image{ public RealImage(URL url) { //加载这个图片 loadImage(url); } public void displayImage() { //显示这个图片 } private void loadImage(URL url) { } } //代理类 public class ProxyImage implements Image{ private URL url; public ProxyImage(URL url) { this.url = url; } //this method delegates to the real image public void displayImage() { RealImage real = new RealImage(url); real.displayImage(); } } //使用代理模式 public class Client { public static void main(String[] args) { Image image = new ProxyImage("test.png"); image.display(); } }
代理模式分为静态代理和动态代理,静态代理的真正实现类是提早写好而且编译好的,动态代理的真正实现类是运行是生成而且编译的,上面的例子使用的是静态代理。
动态代理又分为 JDK动态代理 和 CGLIB动态代理,JDK动态代理是基于接口的动态代理,CGLIB动态代理是基于类的代理。有兴趣的能够找下相关资料。
责任链模式用于管理对象之间的算法,关系和责任,经过将多个不一样处理对象连接在一块儿处理请求,下降耦合度,提升系统灵活性。它属于行为型模式。
它的优势:
一、下降耦合度。
二、简化了对象。
三、加强给对象指派职责的灵活性。
四、增长新的请求处理类很方便。
它的缺点:
一、系统性能将受到必定影响,并且在进行代码调试时不太方便,可能会形成循环调用。
二、可能不容易观察运行时的特征,不方便排错。
例如,咱们用下面这个记录日志的例子来更好的理解责任链模式。
//定义一个抽象的日志接口,而且提供一个能够设置下一个处理日志对象的方法 public abstract class AbstractLogger { public static int INFO = 1; public static int DEBUG = 2; public static int ERROR = 3; protected int level; //责任链中的下一个元素 protected AbstractLogger nextLogger; public void setNextLogger(AbstractLogger nextLogger){ this.nextLogger = nextLogger; } public void logMessage(int level, String message){ if(this.level <= level){ write(message); } if(nextLogger !=null){ nextLogger.logMessage(level, message); } } //抽象方法 abstract protected void write(String message); } //定义一个标准日志的实现类 public class ConsoleLogger extends AbstractLogger { public ConsoleLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("Standard Console::Logger: " + message); } } //定义一个错误日志的实现类 public class ErrorLogger extends AbstractLogger { public ErrorLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("Error Console::Logger: " + message); } } //定义一个文件日志的实现类 public class FileLogger extends AbstractLogger { public FileLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("File::Logger: " + message); } } //使用责任链模式 public class Client { //设置责任链的调用顺序 private static AbstractLogger getChainOfLoggers(){ AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR); AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG); AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO); errorLogger.setNextLogger(fileLogger); fileLogger.setNextLogger(consoleLogger); return errorLogger; } public static void main(String[] args) { AbstractLogger loggerChain = getChainOfLoggers(); loggerChain.logMessage(AbstractLogger.INFO, "info level information."); loggerChain.logMessage(AbstractLogger.DEBUG, "debug level information."); loggerChain.logMessage(AbstractLogger.ERROR, "error information."); } }
命令模式是请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找能够处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。它属于行为型模式。
它的优势:
一、下降了系统耦合度。
二、新的命令能够很容易添加到系统中去。
它的缺点:
使用命令模式可能会致使某些系统有过多的具体命令类。
例如,咱们用下面这个开关灯的例子来更好的理解命令模式。
// 定义执行命令接口 public interface Command{ public void execute(); } //开灯命令实现类 public class LightOnCommand implements Command{ Light light; public LightOnCommand(Light light){ this.light = light; } public void execute(){ light.switchOn(); } } //关灯命令实现类 public class LightOffCommand implements Command{ Light light; public LightOffCommand(Light light){ this.light = light; } public void execute(){ light.switchOff(); } } //真正开关灯的类 public class Light{ private boolean on; public void switchOn(){ on = true; } public void switchOff(){ on = false; } } //根据不一样命令执行开关灯 public class RemoteControl{ private Command command; public void setCommand(Command command){ this.command = command; } public void pressButton(){ command.execute(); } } //调用命令模式 public class Client{ public static void main(String[] args){ RemoteControl control = new RemoteControl(); Light light = new Light(); Command lightsOn = new LightsOnCommand(light); Command lightsOff = new LightsOffCommand(light); //设置开灯命令来开灯 control.setCommand(lightsOn); control.pressButton(); //设置关灯命令来关灯 control.setCommand(lightsOff); control.pressButton(); } }
解释器模式是给定一种语言,定义其语法以及使用该语法来表示语言中句子的解释器。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。它属于行为型模式。
它的优势:
一、可扩展性比较好,灵活。
二、增长了新的解释表达式的方式。
三、易于实现简单语法。
它的缺点:
一、可利用场景比较少。
二、对于复杂的文法比较难维护。
三、解释器模式会引发类膨胀。
四、解释器模式采用递归调用方法。
例如,咱们用下面这个简单规则表达式的例子来更好的理解解释器模式。
//定义一个表达式接口 public interface Expression { public boolean interpret(String context); } //定义一个基本规则的实现,输入的内容包含初始化的内容时,返回 true public class TerminalExpression implements Expression { private String data; public TerminalExpression(String data){ this.data = data; } @Override public boolean interpret(String context) { if(context.contains(data)){ return true; } return false; } } //定义一个或者规则的实现,输入的内容包含初始化时任意一个内容时,返回 true,不然 false public class OrExpression implements Expression { private Expression expr1 = null; private Expression expr2 = null; public OrExpression(Expression expr1, Expression expr2) { this.expr1 = expr1; this.expr2 = expr2; } @Override public boolean interpret(String context) { return expr1.interpret(context) || expr2.interpret(context); } } //定义一个而且规则的实现,输入的内容同时包含初始化时两个内容时,返回 true,不然 false public class AndExpression implements Expression { private Expression expr1 = null; private Expression expr2 = null; public AndExpression(Expression expr1, Expression expr2) { this.expr1 = expr1; this.expr2 = expr2; } @Override public boolean interpret(String context) { return expr1.interpret(context) && expr2.interpret(context); } } //调用规则表达式 public class Client { //规则:Robert 和 John 是男性,输入只要知足其中一个规则就行 public static Expression getMaleExpression(){ Expression robert = new TerminalExpression("Robert"); Expression john = new TerminalExpression("John"); return new OrExpression(robert, john); } //规则:Julie 是一个已婚的女性,输入只要知足两个规则 public static Expression getMarriedWomanExpression(){ Expression julie = new TerminalExpression("Julie"); Expression married = new TerminalExpression("Married"); return new AndExpression(julie, married); } public static void main(String[] args) { Expression isMale = getMaleExpression(); Expression isMarriedWoman = getMarriedWomanExpression(); System.out.println("John is male? " + isMale.interpret("John")); System.out.println("Julie is a married women? " + isMarriedWoman.interpret("Married Julie")); } }
最后,仍是那句话,每种设计模式有它本身的好处,也有它的坏处。在写代码时,多思考,想好在写,省得返工,先从思惟方式上改变。使用单一原则,一个类,一个方法只作一件事情,这样方便维护,耦合低,可扩展。
PS:
清山绿水始于尘,博学多识贵于勤。
微信公众号:「清尘闲聊」。
欢迎一块儿谈天说地,聊代码。