第一章 Java设计模式笔记——七大设计原则
第二章 Java设计模式笔记——六个创建型模式
假如我们去欧洲国家旅游电脑没电了,发现插座用的是两孔的(欧标),我们这时需要买个多功能转换插头 (适配器) ,这样就可以充电了。
与电源适配器相似,在适配器模式中引入了一个被称为适配器(Adapter)的包装类,而它所包装的对象称为适配者(Adaptee),即被适配的类。适配器的实现就是把客户类的请求转化为对适配者的相应接口的调用。也就是说:当客户类调用适配器的方法时,在适配器类的内部将调用适配者类的方法,而这个过程对客户类是透明的,客户类并不直接访问适配者类。因此,适配器让那些由于接口不兼容而不能交互的类可以一起工作。
适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
在类适配器中,它通过实现Target并继承一个Adaptee对象使二者产生联系,在对象适配器中是通过关联一个Adaptee对象使二者产生联系。
以生活中充电器的例子来讲解适配器,充电器本身相当于 Adapter,220V 交流电相当于被适配者,我们的目标是 5V 直流
public interface Voltage5V {int output5v();
}
public class Voltage220V {public int output220V(){System.out.println("被适配者输出:" + 220 + "V");return 220;}
}
public class VoltageAdpter extends Voltage220V implements Voltage5V{@Overridepublic int output5v() {int output220V = output220V();System.out.println("适配者将220V转换为5V输出");return output220V / 44;}
}
public class Client {public static void main(String[] args) {Voltage5V voltage5V = new VoltageAdpter();System.out.println("转换后输出:" + voltage5V.output5v() + "V");}
}
总结
根据“合成复用原则”,在系统中尽量使用关联关系(聚合)来替代继承关系。我们将上述改为对象适配,只需将 Adapter 做修改,将Adaptee 类聚合到 Adapter 就行了。
public class VoltageObjectAdpter implements Voltage5V{private Voltage220V voltage220V;public Voltage220V getVoltage220V() {return voltage220V;}public void setVoltage220V(Voltage220V voltage220V) {this.voltage220V = voltage220V;}@Overridepublic int output5v() {int output220V = voltage220V.output220V();System.out.println("适配者将220V转换为5V输出");return output220V / 44;}
}
缺省适配器模式(Default Adapter Pattern):当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求,它适用于不想使用一个接口中的所有方法的情况,又称为单接口适配器模式。
桥接模式是一种很实用的结构型设计模式,如果软件系统中某个类存在两个独立变化的维度,通过该模式可以将这两个维度分离出来,使两者可以独立扩展,让系统更加符合“单一职责原则”。与多层继承方案不同,它将两个独立变化的维度设计为两个独立的继承等级结构,并且在抽象层建立一个抽象关联,该关联关系类似一条连接两个独立继承结构的桥,故名桥接模式。
桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。
现有各种型号的毛笔进行绘画,在绘画中需要着不同颜色,设计如下:
问题
public interface Color {void coloring();
}
public class RedColor implements Color{@Overridepublic void coloring() {System.out.println("开始着红颜色");}
}
public class BuleColor implements Color{@Overridepublic void coloring() {System.out.println("开始着蓝颜色");}
}
public abstract class Pen {protected Color color;public void setColor(Color color) {this.color = color;}public abstract void drawing();
}
public class BigPen extends Pen{@Overridepublic void drawing() {System.out.println("大号毛笔开始绘画");color.coloring();}
}
public class SmallPen extends Pen{@Overridepublic void drawing() {System.out.println("小号毛笔开始绘画");color.coloring();}
}
public class Client {public static void main(String[] args) {Pen bigPen = new BigPen();bigPen.setColor(new RedColor());bigPen.drawing();}
}
桥接模式的主要优点如下:
桥接模式的主要缺点:
在以下情况下可以考虑使用桥接模式:
对于树形结构,当容器对象(如文件夹)的某一个方法被调用时,将遍历整个树形结构,寻找也包含这个方法的成员对象(可以是容器对象,也可以是叶子对象)并调用执行,牵一而动百,其中使用了递归调用的机制来对整个结构进行处理。由于容器对象和叶子对象在功能上的区别,在使用这些对象的代码中必须有区别地对待容器对象和叶子对象,而实际上大多数情况下我们希望一致地处理它们,因为对于这些对象的区别对待将会使得程序非常复杂。组合模式为解决此类问题而诞生,它可以让叶子对象和容器对象的使用具有一致性。
组合模式(Composite Pattern):组合多个对象形成树形结构以表示具有“整体—部分”关系的层次结构。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,组合模式又可以称为“整体—部分”(Part-Whole)模式,它是一种对象结构型模式。
为了让系统具有更好的灵活性和可扩展性,我们设计一个文件夹的展示功能如下:
public abstract class AbstractFile {protected abstract void add(AbstractFile abstractFile);protected abstract void remove(AbstractFile abstractFile);protected abstract void print();
}
public class ImageFile extends AbstractFile{private String name;public ImageFile(String name) {this.name = name;}@Overrideprotected void add(AbstractFile abstractFile) {System.out.println("不支持该操作");}@Overrideprotected void remove(AbstractFile abstractFile) {System.out.println("不支持该操作");}@Overrideprotected void print() {System.out.println("图片名称:" + name + ".jpg");}
}
public class TextFile extends AbstractFile{private String name;public TextFile(String name) {this.name = name;}@Overrideprotected void add(AbstractFile abstractFile) {System.out.println("不支持该操作");}@Overrideprotected void remove(AbstractFile abstractFile) {System.out.println("不支持该操作");}@Overrideprotected void print() {System.out.println("TxT名称:" + name + ".jpg");}
}
public class Folder extends AbstractFile{private List abstractFileList = new ArrayList<>();private String name;public Folder(String name) {this.name = name;}@Overrideprotected void add(AbstractFile abstractFile) {abstractFileList.add(abstractFile);}@Overrideprotected void remove(AbstractFile abstractFile) {abstractFileList.remove(abstractFile);}@Overrideprotected void print() {System.out.println("****文件夹名称:" + name + "****");for (AbstractFile abstractFile : abstractFileList){abstractFile.print();}}
}
public class Client {public static void main(String[] args) {AbstractFile computer = new Folder("电脑文件夹");AbstractFile txts = new Folder("图片文件夹");AbstractFile imgs = new Folder("txt文件夹");AbstractFile img1 = new ImageFile("香蕉图片");AbstractFile img2 = new ImageFile("苹果图片");AbstractFile txt1 = new TextFile("记录文档");AbstractFile txt2 = new TextFile("笔记文档");computer.add(txts);computer.add(imgs);imgs.add(img1);imgs.add(img2);txts.add(txt1);txts.add(txt2);computer.print();//imgs.print();}
}
组合模式的主要优点如下:
组合模式的主要缺点如下:
在增加新构件时很难对容器中的构件类型进行限制。有时候我们希望一个容器中只能有某些特定类型的对象,例如在某个文件夹中只能包含文本文件,使用组合模式时,不能依赖类型系统来施加这些约束,因为它们都来自于相同的抽象层,在这种情况下,必须通过在运行时进行类型检查来实现,这个实现过程较为复杂。
适用场景:
装饰模式可以在不改变一个对象本身功能的基础上给对象增加额外的新行为,在现实生活中,这种情况也到处存在,例如一张照片,我们可以不改变照片本身,给它增加一个相框,使得它具有防潮的功能,而且用户可以根据需要给它增加不同类型的相框,甚至可以在一个小相框的外面再套一个大相框。
装饰模式是一种用于替代继承的技术,它通过一种无须定义子类的方式来给对象动态增加职责,使用对象之间的关联关系取代类之间的继承关系。在装饰模式中引入了装饰类,在装饰类中既可以调用待装饰的原有类的方法,还可以增加新的方法,以扩充原有类的功能。
装饰模式(Decorator Pattern):动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活。装饰模式是一种对象结构型模式。
现在有一个新的房子,我们需要给房子刷墙、贴砖等,用装饰者模式设计实现如下:
public abstract class House {protected abstract void display();
}
public class Apartment extends House{@Overrideprotected void display() {System.out.println("别墅房子展示");}
}
public class Decorator extends House{private House house;public Decorator(House house) {this.house = house;}@Overrideprotected void display() {house.display();}
}
public class WallDecorator extends Decorator{public WallDecorator(House house) {super(house);}@Overrideprotected void display() {super.display();painting();}public void painting(){System.out.println("刷墙开始");}}
public class Client {public static void main(String[] args) {House apartment = new Apartment();House wallDecorator = new WallDecorator(apartment);wallDecorator.display();}
}
装饰模式的主要优点如下:
装饰模式的主要缺点如下:
在软件开发中,有时候为了完成一项较为复杂的功能,一个客户类需要和多个业务类交互,而这些需要交互的业务类经常会作为一个整体出现,由于涉及到的类比较多,导致使用时代码较为复杂,此时,特别需要一个类似服务员一样的角色,由它来负责和多个业务类进行交互,而客户类只需与该类交互。外观模式通过引入一个新的外观类(Facade)来实现该功能,外观类充当了软件系统中的“服务员”,它为多个业务类的调用提供了一个统一的入口,简化了类与类之间的交互。
外观模式:为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
个人在家生活:每次都需要打开灯、打开电视、打开空调;睡觉时关闭灯、关闭电视、关闭空调;操作起来都比较麻烦。我们可以加入一个智能小爱同学,帮助我们来管理这些家居。
public class Air {public void turnOn(){System.out.println("打开空调...");}public void turnOff(){System.out.println("关闭空调...");}
}
public class Light {public void turnOn(){System.out.println("打开电灯...");}public void turnOff(){System.out.println("关闭电灯...");}
}
public class TV {public void turnOn(){System.out.println("打开电视...");}public void turnOff(){System.out.println("关闭电视...");}
}
public class XiaoAiFacade {private Air air;private Light light;private TV tv;public XiaoAiFacade(Air air, Light light, TV tv) {this.air = air;this.light = light;this.tv = tv;}public void sleep(){System.out.println("小爱同学,我要睡觉了");air.turnOff();light.turnOff();tv.turnOff();}public void getUp(){System.out.println("小爱同学,我起床了");air.turnOn();light.turnOn();tv.turnOn();}
}
public class Client {public static void main(String[] args) {XiaoAiFacade xiaoAiFacade = new XiaoAiFacade(new Air(), new Light(), new TV());xiaoAiFacade.sleep();xiaoAiFacade.getUp();}
}
外观模式的主要优点如下:
外观模式的主要缺点如下:
模式适用场景:
在软件系统中,有时候也会存在资源浪费的情况,例如在计算机内存中存储了多个完全相同或者非常相似的对象,如果这些对象的数量太多将导致系统运行代价过高,内存属于计算机的“稀缺资源”,不应该用来“随便浪费”,享元模式可以用于节约内存使用空间,实现对这些相同或者相似对象的共享访问。享元模式经典的应用场景就是池技术了,String 常量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式
享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。
享元模式提出了两个要求:细粒度和共享对象。这里就涉及到内部状态和外部状态了,即将对象的信息分为两个部分:内部状态和外部状态。
为了节约存储空间,提高系统性能,使用享元模式来设计围棋软件中的棋子,结构如下所示:
public abstract class IgoChessman {public abstract String getColor();public void display(Coordinates coord){System.out.println("棋子颜色:" + this.getColor() + ",棋子位置:" + coord.getX() + "," + coord.getY() );}
}
public class Coordinates {private int x;private int y;public Coordinates(int x, int y) {this.x = x;this.y = y;}public int getX() {return x;}public void setX(int x) {this.x = x;}public int getY() {return y;}public void setY(int y) {this.y = y;}
}
public class BlackIgoChessman extends IgoChessman{@Overridepublic String getColor() {return "黑色";}
}
public class WhiteIgoChessman extends IgoChessman{@Overridepublic String getColor() {return "白色";}
}
public class IgoChessmanFactory {private static IgoChessmanFactory igoChessmanFactory = new IgoChessmanFactory();private static Map map;private IgoChessmanFactory() {map = new HashMap<>();map.put("b", new BlackIgoChessman());map.put("w", new WhiteIgoChessman());}public static IgoChessmanFactory getInstance(){return igoChessmanFactory;}public IgoChessman getIgoChessman(String color) {return map.get(color);}
}
public class Client {public static void main(String[] args) {IgoChessmanFactory igoChessmanFactory = IgoChessmanFactory.getInstance();//生成两颗黑子IgoChessman black1 = igoChessmanFactory.getIgoChessman("b");IgoChessman black2 = igoChessmanFactory.getIgoChessman("b");//生成两颗白子IgoChessman white1 = igoChessmanFactory.getIgoChessman("w");IgoChessman white2 = igoChessmanFactory.getIgoChessman("w");//显示棋子,同时设置位置black1.display(new Coordinates(1, 2));black2.display(new Coordinates(3, 4));white1.display(new Coordinates(5, 6));white2.display(new Coordinates(7, 8));}
}
享元模式的主要优点如下:
享元模式的主要缺点如下:
在以下情况下可以考虑使用享元模式:
在软件开发中,也有一种设计模式可以提供与代购网站类似的功能。由于某些原因,客户端不想或不能直接访问一个对象,此时可以通过一个称之为“代理”的第三者来实现间接访问,该方案对应的设计模式被称为代理模式。
代理模式:给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。
代理模式有不同的形式, 主要有三种静态代理、动态代理
(JDK 代理、接口代理)和 Cglib 代理 (可以在内存动态的创建对象,而不需要实现接口, 他是属于动态代理的范畴) 。
静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类。
我们这里实现一个类似ORM框架的一个功能,结构如下:
public interface ITeacherDao {void teach();
}
public class TeacherDao implements ITeacherDao{@Overridepublic void teach() {System.out.println("老师开始教学");}
}
public class TeacherDaoProxy implements ITeacherDao{private ITeacherDao iTeacherDao;public TeacherDaoProxy(ITeacherDao iTeacherDao) {this.iTeacherDao = iTeacherDao;}@Overridepublic void teach() {System.out.println("开始代理....");iTeacherDao.teach();System.out.println("结束代理....");}
}
public class Client {public static void main(String[] args) {ITeacherDao iTeacherDao = new TeacherDaoProxy(new TeacherDao());iTeacherDao.teach();}
}
代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理;代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象;动态代理也叫做:JDK 代理、接口代。
public interface ITeacherDao {void teach();
}
public class TeacherDao implements ITeacherDao {@Overridepublic void teach() {System.out.println("老师开始教学");}
}
public class ProxyFactory {private Object target; //目标对象public ProxyFactory(Object target) {this.target = target;}public Object getProxyInstance(){return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("JDK 代理开始");return method.invoke(target, args);}});}
}
public class Client {public static void main(String[] args) {ITeacherDao iTeacherDao = (ITeacherDao) new ProxyFactory(new TeacherDao()).getProxyInstance();iTeacherDao.teach();}
}
静态代理和 JDK 代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理,这就是 Cglib 代理。
Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展, 有的也将Cglib代理归属到动态代理。
Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类。
public class TeacherDao {public void teach() {System.out.println("老师开始教学");}
}
public class ProxyFactory implements MethodInterceptor {private Object target;public ProxyFactory(Object target) {this.target = target;}public Object getProxyInstance() {//1. 创建一个工具类Enhancer enhancer = new Enhancer();//2. 设置父类enhancer.setSuperclass(target.getClass());//3. 设置回调函数enhancer.setCallback(this);//4. 创建子类对象,即代理对象return enhancer.create();}@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("cglib 代理开始...");Object val = method.invoke(target, objects);System.out.println("cglib 代理结束...");return val;}
}
public class Client {public static void main(String[] args) {TeacherDao teacherDao = (TeacherDao) new ProxyFactory(new TeacherDao()).getProxyInstance();teacherDao.teach();}
}
以上就是几种创建型模式:适配器模式、桥接模式、组合模式、装饰模式、外观模式、享元模式,代理模式,通过本文学习我们能快速掌握这几种模式。
上一篇:Gin环境搭建
下一篇:Go中 channel的使用