软件设计模式的概念
软件设计模式(Software Design Pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案。也就是说,它是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用。其目的是为了提高代码的可重用性、代码的可读性和代码的可靠性。
一共介绍 7 种设计原则,它们分别为开闭原则、里氏替换原则、依赖倒置原则、单一职责原则、接口隔离原则、迪米特法则和本节所介绍的合成复用原则。
这 7 种设计原则是软件设计模式必须尽量遵循的原则,各种原则要求的侧重点不同。其中,开闭原则是总纲,它告诉我们要对扩展开放,对修改关闭;里氏替换原则告诉我们不要破坏继承体系;依赖倒置原则告诉我们要面向接口编程;单一职责原则告诉我们实现类要职责单一;接口隔离原则告诉我们在设计接口的时候要精简单一;迪米特法则告诉我们要降低耦合度;合成复用原则告诉我们要优先使用组合或者聚合关系复用,少用继承关系复用。
1. 根据目的来分
根据模式是用来完成什么工作来划分,这种方式可分为创建型模式、结构型模式和行为型模式 3 种。
- 创建型模式:用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。GoF 中提供了单例、原型、工厂方法、抽象工厂、建造者等 5 种创建型模式。
- 结构型模式:用于描述如何将类或对象按某种布局组成更大的结构,GoF 中提供了代理、适配器、桥接、装饰、外观、享元、组合等 7 种结构型模式。
- 行为型模式:用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责。GoF 中提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等 11 种行为型模式。
2. 根据作用范围来分
根据模式是主要用于类上还是主要用于对象上来分,这种方式可分为类模式和对象模式两种。
- 类模式:用于处理类与子类之间的关系,这些关系通过继承来建立,是静态的,在编译时刻便确定下来了。GoF中的工厂方法、(类)适配器、模板方法、解释器属于该模式。
- 对象模式:用于处理对象之间的关系,这些关系可以通过组合或聚合来实现,在运行时刻是可以变化的,更具动态性。GoF 中除了以上 4 种,其他的都是对象模式。
设计模式:
1.单例模式的定义与特点
单例(Singleton)模式的定义:指一个类只有一个实例对象,且该类能提供创建这个实例对象的一种模式。(减少内存空间资源的浪费)
单例模式
private A(){
}
private static A a =null //懒汉模式(不创建对象)
//private static A a= new A() //饿汉模式(创建对象)
public static A getInstance(){
if(a==null){
a=new A();
}
return a;
}
当使用懒汉模式时,如果私有化有变量引用,可能会发生线程安全问题,在方法在加synchronized加锁,性能就会下降。采用双重锁机制 if(a==null){ sychronized(A.class){ if(a=null){ a=new A() } } } return a;
由于cpu与jvm出现指令重排,可能当写入时,读到但是还没被写入就执行了。可以加volatile
volatile+双重检测机制 ---》禁止指令重排
private A(){
}
private volatile static A a =null //懒汉模式(不创建对象)
//private static A a= new A() //饿汉模式(创建对象)
public static A getInstance(){
if(a==null){
sychronized(A.class){
if(a=null){
a=new A()
}
}
}
return a;
}
当使用饿汉模式时会可能创建一个不需要的对象,浪费内存影响性能。它是线程安全的
可以使用static静态化创建对象
private A(){
}
//先后顺序要注意
//饿汉模式(创建对象)
private static A a= null;
static{
a=new A();
}
public static A getInstance(){
return a;
}
枚举定义单例(推荐使用,最安全)对于懒汉来说线程安全,饿汉来说不会造成资源浪费
private A(){
}
public static A getInstance(){
return B.INSTANCE.getInstance();
}
private enum B{
INSTANCE;
private A a;
//JVM保证这个方法绝对只调用一次
B(){
a =new A()'
}
public A getInstance(){
return a;
}
}
单例模式的应用场景(对象可共用)
- 当对象可以共享。由于单例模式只允许创建一个对象,共享该对象可以节省内存,加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。
- 当类需要频繁创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
2.原型模式的定义与特点
原型(Prototype)模式:定义一个已实例对象作为原型,通过浅克隆和深克隆把对象复制一份。(无须知道对象的细节)
模式的实现
原型模式的克隆分为浅克隆和深克隆,Java 中的 Object 类提供了浅克隆的 clone() 方法 ,实现Cloneable 重写clone 调用父类方法再强转。
- //具体原型类
- class A implements Cloneable
- Public A(){
- System.out.println("原型创建成功!");
- }
- public Object clone() throws CloneNotSupportedException
- {
- System.out.println("原型复制成功!");
- return (A)super.clone();
- }
- }
- //原型模式的测试类
- public class PrototypeTest
- {
- public static void main(String[] args)throws CloneNotSupportedException
- {
- Realizetype obj1=new Realizetype();
- Realizetype obj2=(Realizetype)obj1.clone();
- System.out.println("obj1==obj2?"+(obj1==obj2));
- }
- }
程序的运行结果如下:
具体原型创建成功!
具体原型复制成功!
obj1==obj2?false
原型模式的应用场景
- 对象之间相同或相似,即只是个别的几个属性不同的时候。
- 对象创建过程比较麻烦,但复制比较简单的时候。
工厂模式的定义与特点
工厂方法(FactoryMethod)模式的定义:将一个种类的类变成一个工厂类,具体实现使用子类实现抽象工厂类的具体方法内容。
工厂方法模式的主要优点有:
- 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;
- 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;
其缺点是:每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。
步骤 1
创建一个接口:
Shape.java
public interface Shape {
void draw();
}
步骤 2
创建实现接口的实体类。
Rectangle.java
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
Square.java
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
Circle.java
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
步骤 3
创建一个工厂,生成基于给定信息的实体类的对象。
ShapeFactory.java
public class ShapeFactory {
//使用 getShape 方法获取形状类型的对象
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
步骤 4
使用该工厂,通过传递类型信息来获取实体类的对象。
FactoryPatternDemo.java
public class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
//获取 Circle 的对象,并调用它的 draw 方法
Shape shape1 = shapeFactory.getShape("CIRCLE");
//调用 Circle 的 draw 方法
shape1.draw();
//获取 Rectangle 的对象,并调用它的 draw 方法
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//调用 Rectangle 的 draw 方法
shape2.draw();
//获取 Square 的对象,并调用它的 draw 方法
Shape shape3 = shapeFactory.getShape("SQUARE");
//调用 Square 的 draw 方法
shape3.draw();
}
}
步骤 5
执行程序,输出结果:
Inside Circle::draw() method.
Inside Rectangle::draw() method.
Inside Square::draw() method.
工厂模式的应用场景
- 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
- 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
- 客户不关心创建产品的细节,只关心产品的品牌。
抽象工厂模式的定义与特点
抽象工厂(AbstractFactory)模式的定义:将一系列同族产品封装到同一个抽象中。如公司可以有不同部门,每个部门有不同产品。
使用抽象工厂模式一般要满足以下条件。
- 系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
- 系统一次只可能消费其中某一族产品,即同族的产品一起使用。
抽象工厂模式除了具有工厂方法模式的优点外,其他主要优点如下。
- 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
- 当增加一个新的产品族时不需要修改原代码,满足开闭原则。
其缺点是:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。
步骤 1
为形状创建一个接口。
Shape.java
public interface Shape {
void draw();
}
步骤 2
创建实现接口的实体类。
Rectangle.java
Rectangle.java
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
Square.java
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
Circle.java
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
步骤 3
为颜色创建一个接口。
Color.java
public interface Color {
void fill();
}
步骤4
创建实现接口的实体类。
Red.java
public class Red implements Color {
@Override
public void fill() {
System.out.println("Inside Red::fill() method.");
}
}
Green.java
public class Green implements Color {
@Override
public void fill() {
System.out.println("Inside Green::fill() method.");
}
}
Blue.java
public class Blue implements Color {
@Override
public void fill() {
System.out.println("Inside Blue::fill() method.");
}
}
步骤 5
为 Color 和 Shape 对象创建抽象类来获取工厂。
AbstractFactory.java
public abstract class AbstractFactory {
public abstract Color getColor(String color);
public abstract Shape getShape(String shape) ;
}
步骤 6
创建扩展了 AbstractFactory 的工厂类,基于给定的信息生成实体类的对象。
ShapeFactory.java
public class ShapeFactory extends AbstractFactory {
@Override
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
@Override
public Color getColor(String color) {
return null;
}
}
ColorFactory.java
public class ColorFactory extends AbstractFactory {
@Override
public Shape getShape(String shapeType){
return null;
}
@Override
public Color getColor(String color) {
if(color == null){
return null;
}
if(color.equalsIgnoreCase("RED")){
return new Red();
} else if(color.equalsIgnoreCase("GREEN")){
return new Green();
} else if(color.equalsIgnoreCase("BLUE")){
return new Blue();
}
return null;
}
}
步骤 7
创建一个工厂创造器/生成器类,通过传递形状或颜色信息来获取工厂。
FactoryProducer.java
public class FactoryProducer {
public static AbstractFactory getFactory(String choice){
if(choice.equalsIgnoreCase("SHAPE")){
return new ShapeFactory();
} else if(choice.equalsIgnoreCase("COLOR")){
return new ColorFactory();
}
return null;
}
}
步骤 8
使用 FactoryProducer 来获取 AbstractFactory,通过传递类型信息来获取实体类的对象。
AbstractFactoryPatternDemo.java
public class AbstractFactoryPatternDemo {
public static void main(String[] args) {
//获取形状工厂
AbstractFactory shapeFactory = FactoryProducer.getFactory("SHAPE");
//获取形状为 Circle 的对象
Shape shape1 = shapeFactory.getShape("CIRCLE");
//调用 Circle 的 draw 方法
shape1.draw();
//获取形状为 Rectangle 的对象
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//调用 Rectangle 的 draw 方法
shape2.draw();
//获取形状为 Square 的对象
Shape shape3 = shapeFactory.getShape("SQUARE");
//调用 Square 的 draw 方法
shape3.draw();
//获取颜色工厂
AbstractFactory colorFactory = FactoryProducer.getFactory("COLOR");
//获取颜色为 Red 的对象
Color color1 = colorFactory.getColor("RED");
//调用 Red 的 fill 方法
color1.fill();
//获取颜色为 Green 的对象
Color color2 = colorFactory.getColor("Green");
//调用 Green 的 fill 方法
color2.fill();
//获取颜色为 Blue 的对象
Color color3 = colorFactory.getColor("BLUE");
//调用 Blue 的 fill 方法
color3.fill();
}
}
步骤 9
执行程序,输出结果:
Inside Circle::draw() method.
Inside Rectangle::draw() method.
Inside Square::draw() method.
Inside Red::fill() method.
Inside Green::fill() method.
Inside Blue::fill() method.
建造者模式的定义与特点
建造者(Builder)模式的定义:将各个部分不同的对象组合成一个复杂的对象过程。
该模式的主要优点如下:
- 各个具体的建造者相互独立,有利于系统的扩展。
- 客户端不必知道产品内部组成的细节,便于控制细节风险。
其缺点如下:
- 产品的组成部分必须相同,这限制了其使用范围。
- 如果产品的内部变化复杂,该模式会增加很多的建造者类。
二 建造者模式分析
1 ) 一个典型的复杂对象其类代码示例如下:
public class Product
{
private String partA; //可以是任意类型
private String partB;
private String partC;
//partA的Getter方法和Setter方法省略
//partB的Getter方法和Setter方法省略
//partC的Getter方法和Setter方法省略
}
2 ) 抽象建造者类中定义了产品的创建方法和返回方法,其典型代码如下:
public abstract class Builder
{
protected Product product=new Product();
public abstract void buildPartA();
public abstract void buildPartB();
public abstract void buildPartC();
public Product getResult()
{
return product;
}
}
3 ) 具体建造者。实现抽象接口,构建和装配各个部件,实例代码如下:
public class ConcreteBuilder extends Builder{
public void buildPartA(){
...
}
public void buildPartB(){
...
}
public void buildPartC(){
...
}
}
4)指挥者类的代码示例如下:
建造者模式的结构中还引入了一个指挥者类Director,该类的作用主要有两个:一方面它隔离了客户与生产过程;另一方面它负责控制产品的生成过程。指挥者针对抽象建造者编程,客户端只需要知道具体建造者的类型,即可通过指挥者类调用建造者的相关方法,返回一个完整的产品对象。
public class Director
{
private Builder builder;
//1 构造方法的方式注入builder对象
public Director(Builder builder)
{
this.builder=builder;
}
//2 set方法注入builder对象
public void setBuilder(Builder builder)
{
this.builder=builer;
}
public Product construct()
{
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
return builder.getResult();
}
}
5 ) 客户端类代码片段:
在客户端代码中,无须关心产品对象的具体组装过程,只需确定具体建造者的类型即可,建造者模式将复杂对象的构建与对象的表现分离开来,这样使得同样的构建过程可以创建出不同的表现。
……
Builder builder = new ConcreteBuilder();
Director director = new Director(builder);
Product product = director.construct();
建造者模式的应用场景
建造者(Builder)模式创建的是复杂对象,其产品的各个部分经常面临着剧烈的变化,但将它们组合在一起的算法却相对稳定,所以它通常在以下场合使用。
- 创建的对象较复杂,由多个部件构成,各部件面临着复杂的变化,但构件间的建造顺序是稳定的。
- 创建复杂对象的算法独立于该对象的组成部分以及它们的装配方式,即产品的构建过程和最终的表示是独立的。
代理模式的定义与特点
代理模式的定义:可能因为某种需要不能直接访问目标对象,这时就需要一个中介对象作为访问对象和目标对象的中介。
代理模式的主要优点有:
- 保护目标对象,扩展目标对象的功能,因分离降低了系统的耦合度;
其主要缺点是:
- 增加多一个中介对象造成请求处理速度变慢,增加了系统的复杂度;
静态代理:目标对象和代理对象都需要实现接口,每次创建目标对象都需要创建一个代理类。
public class ProxyTest
{
public static void main(String[] args)
{
Proxy proxy=new Proxy();
proxy.Request();
}
}
//抽象主题
interface Subject
{
void Request();
}
//真实主题
class RealSubject implements Subject
{
public void Request()
{
System.out.println("访问真实主题方法...");
}
}
//代理
class Proxy implements Subject
{
private RealSubject realSubject;
public void Request()
{
if (realSubject==null)
{
realSubject=new RealSubject();
}
preRequest();
realSubject.Request();
postRequest();
}
public void preRequest()
{
System.out.println("访问真实主题之前的预处理。");
}
public void postRequest()
{
System.out.println("访问真实主题之后的后续处理。");
}
}
动态代理:代理类不再需要实现目标对象接口。
jdk动态代理:目标对象需要实现目标对象接口,运行时jvm在内存中动态创建代理对象。
/**
* 测试类
*/
public class Test {
public static void main(String[] args){
Officer o =new Officer1();
//jvm类加载器
// ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
//Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
//InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法
Officer proxy=(Officer)Proxy.newProxyInstance(Officer.class.getClassLoader(),new Class[]{Officer.class},new JDKProxy(o));
proxy.show();
// Officer proxy = (Officer)new jdkproxy(o).newProxy();
// proxy.show();
}
}
//目标对象:官员
interface Officer{
public void show();
}
class Officer1 implements Officer{
@Override
public void show() {
System.out.println("写奏折");
}
}
/**
* jdk动态代理 实现InvocationHandler调用处理器
*/
class JDKProxy implements InvocationHandler {
//任意对象类型 既目标对象类型
private Object targer;
public JDKProxy(final Object targer){
this.targer=targer;
}
//proxy代理的目标对象 method方法 args数组
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object object = method.invoke(targer,args);
//可以增加额外功能。。。。。。
return object;
}
}
//封装jdk动态代理
class jdkproxy{
//任意对象类型 既目标对象类型
private Object targer;
public jdkproxy(final Object targer){
this.targer=targer;
}
public Object newProxy(){
return Proxy.newProxyInstance(targer.getClass().getClassLoader(), targer.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object object = method.invoke(targer,args);
//可以增加额外功能。。。。。。
return object;
}
});
}
}
cglib动态代理:当一个目标对象没有实现接口或没接口时应用,它是根据字节码创建子类方式实现创建代理对象。(被代理的对象不能使用final修饰)需要加入pring-core-3.2.5.jar的cglib包
/**
* 测试类
*/
public class Test {
public static void main(String[] args){
Officer o =new Officer();
//........
}
}
class Officer{
public void show() {
System.out.println("写奏折");
}
}
/**
* cglib动态代理,实现MethodInterceptor
*/
class CglibProxy implements MethodInterceptor {
//任意对象类型 既目标对象类型
private Object targer;
//给目标对象创建一个代理对象
public Object getProxyInstance(final Object targer){
//1.工具类
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(this.targer.getClass());
//3.设置回调函数
en.setCallback((Callback) this.targer);
//4.创建子类(代理对象)
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object object = method.invoke(obj,args);
// Object object = proxy.invokeSuper(obj, args);
//可以增加额外功能。。。。。。
return object;
}
}
动态代理总结:CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。
代理模式的应用场景
- 远程代理,这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间。
- 虚拟代理,这种方式通常用于要创建的目标对象开销很大时。例如,下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。
- 安全代理,这种方式通常用于控制不同种类客户对真实对象的访问权限。
- 智能指引,主要用于调用目标对象时,代理附加一些额外的处理功能。例如,增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它。
- 延迟加载,指为了提高系统的性能,延迟对目标的加载。例如,Hibernate 中就存在属性的延迟加载和关联表的延时加载。
适配器模式的定义与特点
适配器模式(Adapter)的定义:主要分为类结构模式和对象结构模式,前者耦合度高所以不常用;把一个类或对象原来没有的内容通过一个适配器类或对象从另一个类或对象中提取内容,从而达到两者之间可以通过适配器来转换。如第三方组件接口和自定义接口不兼容和之前可能某些接口定义已经有点符合现在的开发要求。
该模式的主要优点如下。
- 客户端通过适配器可以透明地调用目标接口。
- 复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。
- 将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。
其缺点是:对类适配器来说,更换适配器的实现过程比较复杂。
类适配器:通过继承实现方法达到转换
/**
* 测试类
*/
public class Test {
public static void main(String[] args){
huangzi h=new adapter(); //多态调用父类方法
h.yuxi1();
}
}
/**
* 由于皇帝玉玺只有一个,皇子继位后要拿上任皇帝的玉玺才能发号命令,所以皇子要转换成皇帝
*/
//皇子
interface huangzi{
public void yuxi1();
}
//皇上的玉玺
interface yuxi{
public void yuxi();
}
//先皇
class huangdi implements yuxi{
public void yuxi() {
System.out.println("玉玺在手");
}
}
//适配器:大臣(把原本没有玉玺的皇子变成有玉玺)
class adapter extends huangdi implements huangzi{
public void yuxi1() {
yuxi();//调用父类的方法
}
}
对象适配器:通过组合方式达到,直接通过适配器的构造方法调用目标对象从而使用目标对象方法
/**
* 测试类
*/
public class Test {
public static void main(String[] args){
yuxi y = new huangdi();
huangzi h=new adapter(y); //多态调用父类方法
h.yuxi1();
}
}
/**
* 由于皇帝玉玺只有一个,皇子继位后要拿上任皇帝的玉玺才能发号命令,所以皇子要转换成皇帝
*/
//皇子
interface huangzi{
public void yuxi1();
}
//皇上的玉玺
interface yuxi{
public void yuxi();
}
//先皇
class huangdi implements yuxi{
public void yuxi() {
System.out.println("玉玺在手");
}
}
//适配器:大臣(把原本没有玉玺的皇子变成有玉玺)
class adapter implements huangzi{
private yuxi y;
public adapter(yuxi y){
this.y=y;
}
public void yuxi1() {
y.yuxi();//调用父类的方法
}
}
模式的应用场景
- 之前开发时的内容已满足现在开发的内容,但接口规范不一致,也可以定义一个重用类,通过适配方法调用。
- 第三方组件和自定义要求的接口定义不同。
桥接模式的定义与特点
桥接(Bridge)模式的定义如下:用组合方式代替继承方式,降低耦合度,抽象与实现分离,使它们可以独立变化。
桥接(Bridge)模式的优点是:
- 由于抽象与实现分离,所以扩展能力强,适合于多重继承多维度形式,实现细节透明;
缺点是:主要是针对抽象层做的开发,所以对抽象思维 设计 理解难度较高。
电器中可以有洗衣机,空调 ; 洗衣服和空调又可以分为美的牌,某牌 如果一层层继承子类就太多了。
/**
* 测试类
*/
public class Test {
public static void main(String[] args){
kongtiao k = new meidikongtiao();
meidiguishi m=new meidiguishi(k);
m.s();
}
}
//电器空调
interface kongtiao{
public void save();
}
//美的空调
class meidikongtiao implements kongtiao{
public void save(){System.out.println("美的空调");};
}
//就的空调
class jiukongtiao implements kongtiao{
public void save() {
System.out.println("就的空调");
}
}
//空调类型
class meiditype{
private kongtiao k;
public meiditype( kongtiao k){
this.k=k;
}
public void sell(){
k.save();
}
}
//美的柜式空调
class meidiguishi extends meiditype{
public meidiguishi(kongtiao k) {
super(k);
}
public void s() {
super.sell();
System.out.println("美的柜式空调");
}
}
//美的吊式空调
class meididiaoshi extends meiditype{
public meididiaoshi(kongtiao k) {
super(k);
}
public void s() {
super.sell();
System.out.println("美的吊式空调");
}
}
桥接模式的应用场景
桥接模式通常适用于以下场景。
当类中存在多个维度方向,多重继承,添加灵活性。
装饰模式的定义与特点
装饰(Decorator)模式的定义:不改变对象结构前提下,动态添加对象的功能。即对象结构模式
装饰(Decorator)模式的主要优点有:
- 扩展对象功能比较继承灵活性好,设计不同子类可以创造不同行为的组合。
其主要缺点是:增加了许多子类,使程序变得很复杂。
接口:雍正皇帝 具体接口实现:使用权利 装饰类:设立军机处 具体装饰类实现:批阅 (添加雍正权利功能)
/**
* 测试类
*/
public class Test {
public static void main(String[] args){
yongzheng y=new yongzhengimpl(); //目标对象
junjichu j = new junjichuimpl(y);//调用装饰器
j.ql();
}
}
//雍正皇帝
interface yongzheng{
public void ql();
}
// 具体接口实现:使用权利
class yongzhengimpl implements yongzheng{
public void ql(){System.out.println("杀无赦");};
}
//装饰接口:设立军机处
class junjichu implements yongzheng{
public yongzheng y;
public junjichu(yongzheng y){
this.y=y;
}
public void ql() {
y.ql();
}
}
//具体装饰类实现:批阅
class junjichuimpl extends junjichu{
public junjichuimpl(yongzheng y) {
super(y);
}
public void ql() {
System.out.println("有贪官贪赃枉法");
y.ql();
}
}
装饰模式的应用场景 (java中i/o流就是使用装饰模式添加类功能)
- 当要给类添加功能又不能使用子类方法实现时。
- 当多重继承添加功能造成系统复杂性时
- 当对象的功能需要动态添加或删除时。
外观模式的定义与特点
外观(Facade)模式的定义:多个子系统对外提供一个统一接口访问,这样就可以不需要关心子系统内部细节实现。(降低系统应用复杂性,提高可维护性)
外观(Facade)模式是“迪米特法则”的典型应用,它有以下主要优点。
- 降低耦合度,使得子系统的变化不会影响外部(客户端)类。
- 不用关心子系统内部细节,可以很容易调用方法。
- 降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。
外观(Facade)模式的主要缺点如下。
- 没有很好地限制客户端使用子系统类。
- 增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。
/**
* 测试类
*/
public class Test {
public static void main(String[] args){
Facade f=new Facade();
f.method();
}
}
//外观角色
class Facade
{
private SubSystem01 obj1=new SubSystem01();
private SubSystem02 obj2=new SubSystem02();
private SubSystem03 obj3=new SubSystem03();
public void method()
//可以进行一些逻辑判断进行具体调用那些对象
{
obj1.method1();
obj2.method2();
obj3.method3();
}
}
//子系统角色
class SubSystem01
{
public void method1()
{
System.out.println("子系统01的method1()被调用!");
}
}
//子系统角色
class SubSystem02
{
public void method2()
{
System.out.println("子系统02的method2()被调用!");
}
}
//子系统角色
class SubSystem03
{
public void method3()
{
System.out.println("子系统03的method3()被调用!");
}
}
外观模式的应用场景
- 对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系。
- 当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问。
- 当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性。
享元模式的定义与特点
享元(Flyweight)模式的定义:运用共享技术来达到大量细粒度对象的复用。如共享对象内存减少系统资源浪费。池对象
享元模式的主要优点是:相同对象只要保存一份,降低了因为系统中对象的数量而给内存带来的压力。
其主要缺点是:
- 为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
- 读取享元模式的外部状态会使得运行时间稍微变长。
主要核心:内部状态即可以共享状态直接放内部就可以,外部状态即可能有一些状态数据对象参数不一致,可以通过构造函数传参(外部状态)方法实现。享元工厂类用来存在对象数量等,获取或创建对象。
public class FlyweightPattern
{
public static void main(String[] args)
{
FlyweightFactory factory=new FlyweightFactory();
Flyweight f01=factory.getFlyweight("a");
Flyweight f02=factory.getFlyweight("a");
Flyweight f03=factory.getFlyweight("a");
Flyweight f11=factory.getFlyweight("b");
Flyweight f12=factory.getFlyweight("b");
f01.operation(new UnsharedConcreteFlyweight("第1次调用a。"));
f02.operation(new UnsharedConcreteFlyweight("第2次调用a。"));
f03.operation(new UnsharedConcreteFlyweight("第3次调用a。"));
f11.operation(new UnsharedConcreteFlyweight("第1次调用b。"));
f12.operation(new UnsharedConcreteFlyweight("第2次调用b。"));
}
}
//非享元角色 (外部状态)
class UnsharedConcreteFlyweight
{
private String info;
UnsharedConcreteFlyweight(String info)
{
this.info=info;
}
public String getInfo()
{
return info;
}
public void setInfo(String info)
{
this.info=info;
}
}
//抽象享元角色
interface Flyweight
{
public void operation(UnsharedConcreteFlyweight state);
}
//具体享元角色
class ConcreteFlyweight implements Flyweight
{
private String key;
ConcreteFlyweight(String key)
{
this.key=key;
System.out.println("具体享元"+key+"被创建!");
}
public void operation(UnsharedConcreteFlyweight outState)
{
System.out.print("具体享元"+key+"被调用,");
System.out.println("非享元信息是:"+outState.getInfo());
}
}
//享元工厂角色
class FlyweightFactory
{
//存对象的map
private HashMap<String, Flyweight> flyweights=new HashMap<String, Flyweight>();
//获取对象 没有就创建再存map里
public Flyweight getFlyweight(String key)
{
Flyweight flyweight=(Flyweight)flyweights.get(key);
if(flyweight!=null)
{
System.out.println("具体享元"+key+"已经存在,被成功获取!");
}
else
{
flyweight=new ConcreteFlyweight(key);
flyweights.put(key, flyweight);
}
return flyweight;
}
}
享元模式的应用场景 (池对象是典型例子)
- 系统中存在大量相同或相似的对象,这些对象耗费大量的内存资源。
- 大部分的对象可以按照内部状态进行分组,且可将不同部分外部化,这样每一个组只需保存一个内部状态。
- 由于享元模式需要额外维护一个保存享元的数据结构,所以应当在有足够多的享元实例时才值得使用享元模式。
组合模式的定义与特点
组合(Composite)模式的定义:但两者之间的关系属于整体-部分关系。
组合模式的主要优点有:
- 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
- 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;
其主要缺点是:
- 设计较复杂,客户端需要花更多时间理清类之间的层次关系;
- 不容易限制容器中的构件;
- 不容易用继承的方法来增加构件的新功能;
/**
* 测试类
*/
public class Test {
public static void main(String[] args){
Treeye y1=new Treeye("树叶1");
Treeye y2=new Treeye("树叶2");
Treeye y3=new Treeye("树叶3");
Treeye y4=new Treeye("树叶4");
Treeye y5=new Treeye("树叶5");//部分
Treezi t=new Treezi();//整体
t.add(y1);//添加树叶
t.add(y2);
t.add(y3);
t.add(y4);
t.add(y5);
t.count();
}
}
/**
* 组合模式 整体:树 部分:树枝 再部分:树叶
*/
//树
interface Tree{
public void count();//计算有多少树叶
}
//树枝
class Treezi implements Tree
{
//定义集合存树叶
List<Treeye> t = new ArrayList<Treeye>();
//添加树叶
public void add(Treeye y){
t.add(y);
}
//删除树叶
public void remove(Treeye y){
t.remove(y);
}
public void count()
{
System.out.println("树叶总数"+t.size());
}
}
//树叶
class Treeye implements Tree
{
private String name;
public Treeye(String name){
this.name=name;
}
public void count()
{
System.out.println("树叶名字"+name);
}
}
组合模式的应用场景
- 在需要表示一个对象整体与部分的层次结构的场合。
- 要求对用户隐藏组合对象与单个对象的不同,用户可以用统一的接口使用组合结构中的所有对象的场合。
模板模式的定义与特点
模板方法(Template Method)模式的定义如下:将一个抽象类中一系列操作算法,有某些地方算法不同,但大多数是操作算法一致,通过子类实现不同部分。这属于类行为模式
该模式的主要优点如下。
- 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
- 它在父类中提取了公共的部分代码,便于代码复用。
- 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
该模式的主要缺点如下。
- 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。
- 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
/**
* 测试类
*/
public class Test {
public static void main(String[] args){
Person m = new XiaoMing();
Person j = new XiaoJiu();
m.live();
j.live();
}
}
/**
* 模板方法模式 人每天生活大部分一样 要起床 工作(不同的人工作内容不一样) 睡觉
*/
//抽象人每天的生活
abstract class Person
{
//每天生活
public void live(){
go();
work();
sleep();
}
//起床
public void go(){
System.out.println("起床");
}
//工作(因为不同需要子类自己实现)
public abstract void work();
//睡觉
public void sleep(){ System.out.println("睡觉");
}
}
//小明
class XiaoMing extends Person
{
public void work() {
System.out.println("小明搬砖中");
}
}
//小就
class XiaoJiu extends Person
{
public void work() {
System.out.println("小就管理员工中");
}
}
模板模式的应用场景
- 算法整体固定,只有部分会变化,把变化部分抽象出来由子类去实现。
- 当多个子类存在公共的行为时,可以将其提取出来并集中到一个公共父类中以避免代码重复。首先,要识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
- 当需要控制子类的扩展时,模板方法只在特定点调用钩子操作,这样就只允许在这些点进行扩展。
策略模式的定义与特点
策略(Strategy)模式的定义:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。(对于同一个问题能有不同处理实现,多种实现可以互相转换,又不影响客户端使用)
策略模式的主要优点如下。
- 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句。
- 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
- 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
- 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
- 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。
其主要缺点如下。
- 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
- 策略模式造成很多的策略类。
/**
* 测试类
*/
public class Test {
public static void main(String[] args){
Kill k1=new DuSha();//毒杀对象
Kill k2=new QiangSha();//枪杀对象
XiaoJiu x = new XiaoJiu();//环境类
x.setK(k1);
x.kill();//使用策略方法
x.setK(k2);
x.kill();
}
}
/**
* 策略模式 杀人有不同手段方式(如 毒杀 枪杀 等等)
*/
//抽象策略类:杀人
interface Kill
{
//手段方法
public void kill();
}
//具体策略类:毒杀
class DuSha implements Kill
{
public void kill() {
System.out.println("毒杀中");
}
}
//具体策略类:枪杀
class QiangSha implements Kill
{
public void kill() {
System.out.println("枪杀中");
}
}
//环境类: 客户端使用(维护策略对象方法)
class XiaoJiu
{
private Kill k;//设置策略类
public Kill getK() {
return k;
}
public void setK(Kill k) {
this.k = k;
}
//客户端使用策略方法
public void kill() {
k.kill();
}
}
策略模式的应用场景
策略模式在很多地方用到,如 Java SE 中的容器布局管理就是一个典型的实例,Java SE 中的每个容器都存在多种布局供用户选择。
- 一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。
- 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。
- 系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。
- 系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。
- 多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为。
命令模式的定义与特点
命令(Command)模式的定义如下:将一个请求封装成一个对象,通过该命令对象使得请求者和实现者分离而耦合度低。
命令模式的主要优点如下。
- 降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。
- 增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
- 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
- 方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。
其缺点是:可能产生大量具体命令类。因为计对每一个具体操作都需要设计一个具体命令类,这将增加系统的复杂性。
大臣(请求者)请求救济百姓 皇帝(实现者也就是接收者)下达命令(命令对象)准就。
/**
* 测试类
*/
public class Test {
public static void main(String[] args){
DaCheng d =new DaCheng();//请求者对象
d.setK(new ZHUEN(new Huangdi() ) );//内部封装命令对象 再内部封装接收者对象
d.content();
}
}
/**
* 命令模式 通过组合方式解耦合 命令类内部维护接收者类 请求者内部维护一个抽象命令类
*/
//抽象命令:皇帝命令内容
interface Kill
{
//内容
public void content();
}
//具体命令类:准就
class ZHUEN implements Kill
{
//内部维护一个接收者类
private Huangdi h;
public ZHUEN(Huangdi h){
this.h=h;
}
public void content() {
h.kill();
}
}
//接收者:皇帝类
class Huangdi
{
public void kill() {
System.out.println("准就");
}
}
//请求者:大臣
class DaCheng
{
private Kill k;//设置策略类
public Kill getK() {
return k;
}
public void setK(Kill k) {
this.k = k;
}
//最终调用的方法
public void content() {
k.content();
}
}
命令模式的应用场景
- 当系统需要将请求调用者与请求接收者解耦时,命令模式使得调用者和接收者不直接交互。
- 当系统需要随机请求命令或经常增加或删除命令时,命令模式比较方便实现这些功能。
- 当系统需要执行一组操作时,命令模式可以定义宏命令来实现该功能。
- 当系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作时,可以将命令对象存储起来,采用备忘录模式来实现。
责任链模式的定义与特点(职责链模式)
责任链(Chain of Responsibility)模式的定义:将多个请求实现者放到一个执行链中,当发送请求过来,将请求放到执行链中,直到有对象执行为止。无需关系执行链中对象内容细节和请求传递过程 所以是解耦的。
责任链模式是一种对象行为型模式,其主要优点如下。
- 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
- 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
- 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
- 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
- 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
其主要缺点如下。
- 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
- 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
- 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
医院可以医疗不同类型病因 区医院可以治疗感冒发烧 市医院可以治疗重伤 省医院可以治疗疑难绝症
/**
* 测试类
*/
public class Test {
public static void main(String[] args){
Hospital h1= new QuHospital();
Hospital h2= new ShiHospital();
Hospital h3= new ShengHospital();
h1.setH(h2);//区医院内部指向市医院
h2.setH(h3);//市医院内部指向省医院
h1.query("重伤");//放到执行链中执行了
h1.query("重伤100%");//放到执行链中执行了
}
}
/**
* 责任链模式 主要通过抽象对象定义相同事情,再通过一个类对象内部封装另一个类对象的引用一直执行下去,直到执行完成
*/
//医院类
abstract class Hospital
{
private Hospital h;
public Hospital getH() {
return h;
}
public void setH(Hospital h) {
this.h = h;
}
//根据病因合理送到不同医院
public abstract void query(String content);
}
//区医院
class QuHospital extends Hospital
{
public void query(String content) {
//逻辑处理
if("感冒".equals((content))){
System.out.println("区医院就可以");
}else {
//要交给另外对象处理或结束
if(getH()!=null){
//别的子类即自己引用的对象处理
getH().query(content);
}else {
System.out.println("等死吧");
}
}
}
}
//市医院
class ShiHospital extends Hospital
{
public void query(String content) {
//逻辑处理
if("重伤".equals((content))){
System.out.println("市医院就可以");
}else {
//要交给另外对象处理或结束
if(getH()!=null){
//别的子类即自己引用的对象处理
getH().query(content);
}else {
System.out.println("等死吧");
}
}
}
}
//省医院
class ShengHospital extends Hospital
{
public void query(String content) {
//逻辑处理
if("疑难绝症".equals((content))){
System.out.println("省医院就可以");
}else {
//要交给另外对象处理或结束
if(getH()!=null){
//别的子类即自己引用的对象处理
getH().query(content);
}else {
System.out.println("等死吧");
}
}
}
}
责任链模式的应用场景
- 有多个对象可以处理一个请求,哪个对象处理该请求由运行时刻自动确定。
- 可动态指定一组对象处理请求,或添加新的处理者。
- 在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。
状态模式的定义与特点
状态(State)模式的定义:对于有状态对象,多重if else逻辑判断,状态对象在其内部状态发生改变时改变其行为,这些状态可以相互转换,但也不是毫无关系。(如 生病和休息这2个状态)
状态模式是一种对象行为型模式,其主要优点如下。
- 将有状态对象的状态抽象出来,不同状态之间分离,满足单一原则。
- 将每个状态组成一个独立的类,减少对象内部的依赖性。
- 通过增加状态对象的方式就可以简单转换和定义状态,扩展性好,满足开闭原则。
状态模式的主要缺点如下。
添加类的个数可以会很多,从而使程序更复杂。
/**
* 测试类
*/
public class Test {
public static void main(String[] args){
A a=new A();
//这样的就可以添加一个if条件就增加一个子类 创建对象再调用方法就可以了
Pay wechat=new WeChat();
Pay zfb=new ZFB();
Pay band=new Band();
wechat.pay(a ,"2");
zfb.pay(a ,"2");
band.pay(a ,"2");
}
}
/**
* 状态模式 某支付行业中可以有多种支付状态如type 微信:1 支付宝:2 银联:3 将支付状态抽象出来 再通过某类
*/
//抽象支付状态
interface Pay
{
//根据type类型决定用那种支付
public void pay(A a,String type);
}
//微信
class WeChat implements Pay
{
public void pay(A a,String type) {
a.setP(this);//这样就能又调用自身方法
if("1".equals(type)){
System.out.println("微信支付");
}
}
}
//支付宝
class ZFB implements Pay
{
public void pay(A a,String type) {
a.setP(this);
if("2".equals(type)){
System.out.println("支付宝支付");
}
}
}
//银联
class Band implements Pay
{
public void pay(A a,String type) {
a.setP(this);
if("3".equals(type)){
System.out.println("银联支付");
}
}
}
//A公司实现对于可变支付状态的支付方式 先是维护一个抽象状态对象 再通过抽象状态对象维护自己本身对象
class A {
//还是通过内部维护一个状态对象
private Pay p;
//通过构造函数设置
public A(){
p=null;
}
//通过get set赋值
public Pay getP() {
return p;
}
public void setP(Pay p) {
this.p = p;
}
}
状态模式的应用场景
- 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
- 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。
观察者模式的定义与特点
观察者(Observer)模式的定义:可能多个对象存在一对多关系,当一这个对象状态改变时,其他依赖它的对象也会被通知到并自动更新,属于对象行为模式。(这样的模式又叫订阅-发布模式 ,模型-视图模式)
观察者模式是一种对象行为型模式,其主要优点如下。
- 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
- 目标与观察者之间建立了一套触发机制。
它的主要缺点如下。
- 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
- 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
/**
* 测试类
*/
public class Test {
public static void main(String[] args){
Government government=new Decree();//目标对象 主角
Thing t1=new House();//观察者对象 被影响的东西
Thing t2=new IT();//观察者对象 被影响的东西
government.add(t1);
government.add((t2));
government.content();//这个抽象方法会影响其他对象的行为
}
}
/**
* 观察者模式 政府的一个政令会影响房地产,互联网,人民生活质量等等
*/
//抽象目标:政府
abstract class Government
{
//通过集合维护一些观察者的对象即会影响其他变化的抽象对象
public List<Thing> thing = new ArrayList<Thing>();
//添加观察者对象
public void add(Thing t){
thing.add(t);
}
//删除观察者对象
public void remove(Thing t){
thing.remove(t);
}
//抽象方法 即会影响千万人民政令内容 可传入参数 观察者通过参数做正确反映行为
public abstract void content();
}
//具体抽象:政府的政令
class Decree extends Government
{
public void content() {
for(Object t: thing){
((Thing)t).response();
}
}
}
//抽象观察者:事物
interface Thing
{
//一些事情
public void response() ;
}
//房地产
class House implements Thing
{
public void response() {
System.out.println("破产了");
}
}
//互联网
class IT implements Thing{
public void response() {
System.out.println("增值了");
}
}
观察者模式的应用场景
- 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
- 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
中介者模式的定义与特点
中介者(Mediator)模式的定义:定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。
中介者模式是一种对象行为型模式,其主要优点如下。
- 降低了对象之间的耦合性,使得对象易于独立地被复用。
- 将对象间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。
其主要缺点是:当同事类太多时,中介者的职责将很大,它会变得复杂而庞大,以至于系统难以维护。
public class MediatorPattern
{
public static void main(String[] args)
{
Mediator md=new ConcreteMediator();
Colleague c1,c2;
c1=new ConcreteColleague1();
c2=new ConcreteColleague2();
md.register(c1);
md.register(c2);
c1.send();
System.out.println("-------------");
c2.send();
}
}
//抽象中介者
abstract class Mediator
{
public abstract void register(Colleague colleague);
public abstract void relay(Colleague cl); //转发
}
//具体中介者
class ConcreteMediator extends Mediator
{
private List<Colleague> colleagues=new ArrayList<Colleague>();
public void register(Colleague colleague)
{
if(!colleagues.contains(colleague))
{
colleagues.add(colleague);
colleague.setMedium(this);
}
}
public void relay(Colleague cl)
{
for(Colleague ob:colleagues)
{
if(!ob.equals(cl))
{
((Colleague)ob).receive();
}
}
}
}
//抽象同事类
abstract class Colleague
{
protected Mediator mediator;
public void setMedium(Mediator mediator)
{
this.mediator=mediator;
}
public abstract void receive();
public abstract void send();
}
//具体同事类
class ConcreteColleague1 extends Colleague
{
public void receive()
{
System.out.println("具体同事类1收到请求。");
}
public void send()
{
System.out.println("具体同事类1发出请求。");
mediator.relay(this); //请中介者转发
}
}
//具体同事类
class ConcreteColleague2 extends Colleague
{
public void receive()
{
System.out.println("具体同事类2收到请求。");
}
public void send()
{
System.out.println("具体同事类2发出请求。");
mediator.relay(this); //请中介者转发
}
}
中介者模式的应用场景
- 当对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用时。
- 当想创建一个运行于多个类之间的对象,又不想生成新的子类时。
迭代器模式的定义与特点
迭代器(Iterator)模式的定义:不暴露对象内部,提供另一个对象访问该对象的一系列数据。
迭代器模式是一种对象行为型模式,其主要优点如下。
- 访问一个聚合对象的内容而无须暴露它的内部表示。
- 遍历任务交由迭代器完成,这简化了聚合类。
- 它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
- 增加新的聚合类和迭代器类都很方便,无须修改原有代码。
- 封装性良好,为遍历不同的聚合结构提供一个统一的接口。
其主要缺点是:增加了类的个数,这在一定程度上增加了系统的复杂性。
/**
* 测试类
*/
public class Test {
public static void main(String[] args){
Government government=new GovernmentImpl();
government.add("123");
government.add("456");
government.add("789");
Leader leader = government.getIterator();
while (leader.hasNext()){
Object o =leader.next();
System.out.println("==="+o.toString());
}
}
}
/**
* 迭代器模式 通过一个迭代器对象去访问另外对象的集合一系列数据 如领导访问在职公司的每位员工
*/
//聚合对象:公司
interface Government
{
//添加观察者对象
public void add(Object t);
//删除观察者对象
public void remove(Object t);
public Leader getIterator();//内部维护一个迭代器对象
}
//具体抽象:政府的政令
class GovernmentImpl implements Government
{
public List<Object> list = new ArrayList<Object>();
public void add(Object t) {
list.add(t);
}
public void remove(Object t) {
list.remove(t);
}
public Leader getIterator() {
return (new LeaderIterator(list));
}
}
//迭代器对象:领导
interface Leader
{
//开始指针是指向第一个元素的上方
boolean hasNext();//判断是否有下一个元素
Object next();//获取下一个元素
}
//房地产
class LeaderIterator implements Leader
{
//集合对象
private List<Object> list = null;
private int index = -1;//指针位置
public LeaderIterator( List<Object> list ){
this.list=list;
}
public boolean hasNext() {
if(index<list.size()-1){
return true;
}else {
return false;
}
}
public Object next() {
Object obj = null;
index +=1;
if(this.hasNext()){
obj = list.get(index);//使得index是动态变化 从而使hasNext方法的index动态判断
}
return obj;
}
}
迭代器模式的应用场景
迭代器模式通常在以下几种情况使用。
- 当需要为聚合对象提供多种遍历方式时。
- 当需要为遍历不同的聚合结构提供一个统一的接口时。
- 当访问一个聚合对象的内容而无须暴露其内部细节的表示时。
由于聚合与迭代器的关系非常密切,所以大多数语言在实现聚合类时都提供了迭代器类,因此大数情况下使用语言中已有的聚合类的迭代器就已经够了。
访问者模式的定义与特点
访问者(Visitor)模式的定义:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。
访问者(Visitor)模式是一种对象行为型模式,其主要优点如下。
- 扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
- 复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
- 灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
- 符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。
访问者(Visitor)模式的主要缺点如下。
- 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
- 破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。
- 违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。
/**
* 测试类
*/
public class Test {
public static void main(String[] args){
Government government=new Leader();
Work w1 =new Attitude();
w1.accept(government);
}
}
/**
* 访问者模式 公司中老板和员工,老板只关心员工工作有没有做好,具体过程怎么不关心;员工只关心工作后工资是否准时发,完成工作的过程(工作完成态度和完成工作过程)
*/
//抽象访问者:公司
interface Government
{
//工作完成态度
public void attitude(Work work);//具体抽象元素对象
//完成工作过程
public void process(Work work);//具体抽象元素对象
}
//具体访问者1:老板
class Leader implements Government
{
public void attitude(Work work) {
System.out.println("工作做好没");
}
public void process(Work work) {
System.out.println("我不关心你用什么做");
}
}
//具体访问者2:员工
class Staff implements Government
{
public void attitude(Work work) {
System.out.println("工资准时发吗?");
}
public void process(Work work) {
System.out.println("采用某技术做的");
}
}
//抽象元素:工作
interface Work
{
void accept(Government government);//接受一个抽象访问者对象
}
//具体抽象元素1:态度
class Attitude implements Work
{
public void accept(Government government) {
government.attitude(this);
}
}
//具体抽象元素2:过程
class Process implements Work
{
public void accept(Government government) {
government.process(this);
}
}
//对象结构角色:工作集
class SetWork
{
private List<Work> list=new ArrayList<Work>();
public void accept(Government visitor)
{
Iterator<Work> i=list.iterator();
while(i.hasNext())
{
((Work) i.next()).accept(visitor);
}
}
public void add(Work element)
{
list.add(element);
}
public void remove(Work element)
{
list.remove(element);
}
}
访问者模式的应用场景
通常在以下情况可以考虑使用访问者(Visitor)模式。
- 对象结构相对稳定,但其操作算法经常变化的程序。
- 对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构。
- 对象结构包含很多类型的对象,希望对这些对象实施一些依赖于其具体类型的操作。
备忘录模式的定义与特点
备忘录(Memento)模式的定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。(又叫快照模式)
备忘录模式是一种对象行为型模式,其主要优点如下。
- 提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。
- 实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。
- 简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。
其主要缺点是:资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。
/**
* 测试类
*/
public class Test {
public static void main(String[] args){
Originatort originatort=new Originatort();//发起者:我
originatort.setContent("123");//设置内容
final Content contents = originatort.getContents();//创建备忘录内容,返回备忘录对象
Leader leader =new Leader();//管理者:软件
leader.setMemento(contents);//保存内容
originatort.setContent("456");
System.out.println("新内容===="+originatort.getContent());
originatort.recovery(leader.getMemento());//恢复内容(它是通过管理者获取到备忘录对象然后获取备忘录对象内容在重新设值的过程)
System.out.println("恢复内容===="+originatort.getContent());
}
}
/**
* 备忘录模式 在一个软件中通过有恢复内容信息的功能
*/
//备忘录对象:内容信息对象(提供存储信息功能)
class Content
{
private String content;//内容
public Content(String content){
this.content=content;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
//发起者: 即操作软件内容的人(提供创建备忘录内容和恢复备忘录内容的功能)
class Originatort
{
private String content;//内容
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
//创建备忘录内容
public Content getContents(){
return new Content(content);
}
//恢复内容
public void recovery(Content c){
this.setContent(c.getContent());//获取内容在重新赋值
}
}
//管理者:即是软件(提供保存备忘录内容和获取备忘录内容功能)但它不能修改和访问内容
class Leader
{
private Content content;//维护一个备忘录对象
//保存内容
public void setMemento(Content c)
{
content=c;
}
//获取内容
public Content getMemento()
{
return content;
}
}
备忘录模式的应用场景
- 需要保存与恢复数据的场景,如玩游戏时的中间结果的存档功能。
- 需要提供一个可回滚操作的场景,如 Word、记事本、Photoshop,Eclipse 等软件在编辑时按 Ctrl+Z 组合键,还有数据库中事务操作。
解析器模式的定义与特点
解释器(Interpreter)模式的定义:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。
解释器模式是一种类行为型模式,其主要优点如下。
- 扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
- 容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。
解释器模式的主要缺点如下。
- 执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
- 会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
- 可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。
解释器模式包含以下主要角色。
- 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
- 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
- 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
- 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
- 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。
//抽象表达式类
interface AbstractExpression
{
public Object interpret(String info); //解释方法
}
//终结符表达式类
class TerminalExpression implements AbstractExpression
{
public Object interpret(String info)
{
//对终结符表达式的处理
}
}
//非终结符表达式类
class NonterminalExpression implements AbstractExpression
{
private AbstractExpression exp1;
private AbstractExpression exp2;
public Object interpret(String info)
{
//非对终结符表达式的处理
}
}
//环境类
class Context
{
private AbstractExpression exp;
public Context()
{
//数据初始化
}
public void operation(String info)
{
//调用相关表达式类的解释方法
}
}
解析器模式的应用场景
- 当语言的文法较为简单,且执行效率不是关键问题时。
- 当问题重复出现,且可以用一种简单的语言来进行表达时。
- 当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候,如 XML 文档解释。
注意:解释器模式在实际的软件开发中使用比较少,因为它会引起效率、性能以及维护等问题。如果碰到对表达式的解释,在 Java 中可以用 Expression4J 或 Jep 等来设计。