A pattern is a successful or efficient solution recurring problem within a context.
软件模式的基本结构由4个部分组成:
设计模式:是在特定环境下为解决某一种通用软件设计问题提供的一套定制的解决方案,该方案描述了对象和类之间的相互作用
直接说关键元素:
设计模式一般有两种分类方式:
用了就知道了
所谓的GoF(Gang of Four)四个基佬弄的,挺厉害的。
送给读者:说道原则了,虽然这些原则比较简单,很容易理解,但是,对于在实战中,你要经常能够潜意识的想到,比如spring框架中所用到的设计模式,也能够帮助你记住常用的设计模式,所以即使很简单,慢慢的体会还是很有必要的。
单一职责原则:一个对象应该只包含单一的原则,并且该职责被完整地封装在一个类中
单一职责原则将不同开发人员的职责分开,使其任务粒子化,简单的说,就是把任务分明确,并且并且各自的任务比较单一,各干各的活就ok了(自己的理解)
软件实体应当对扩展开放,对修改关闭
举个栗子:讲到动态代理时,比如一个项目,是不是把程序中安全、日志等功能按模块提取了出来,然后把这些方法封装到了一个类中,然后通过aop思想横向切进去,当然你也可以再添加一些其他的功能,不只是日志这些。所谓的对外扩展开放就是你不能修改软件实体代码,但是你可以通过切面添加一些功能。(自己的理解)
所有引用基类的地方必须能透明的使用其子类的对象
意思就是比如你爸爸是个农民,可以干一些简单的体力活。你是你爸的儿子(继承),你很有出息,考上了大学,你就多了一项能看懂知识的这一项技能,因为你继承了你爸的基因,同样你也可以干一些简单的体力活。你可以代替你爸爸,因为你既可以读书,还可以干活。但是你爸爸不能替换你,因为你爸爸对知识不敏感。(这里没有任何鄙视的意思,因为我也是农民的儿子)
依赖倒转原则:高层模块不应该依赖低层模块,它们都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
依赖倒转原则就是面向接口编程。
一个具体类应当只实现接口或抽象类中声明的过得方法,而不要给出多余的方法,否则将无法调用到在子类中增加的新方法。
客户端不应该依赖于那些它不需要的接口
当一个接口太大时,需要将接口分割成一些更细小的接口,使用该接口的客户端仅需要知道与之相关的方法即可。当然接口之间都是隔离的,自己有自己的事情。当客户端实现接口时,看到了不应该看的方法,所以要分割小喽。(自己的理解)
优先使用对象组合,而不是通过继承来达到复用的目的
这个也好理解:就是尽量使用组合关系,对于同一级别、适合、需要组合就可以将这些对象组合在一块,而不需要继承来把这些对象放在一块(因为你继承了,也就相当于你也有了其他的对象,也就是和它们在一块儿),因为继承的话只能单继承。
迪米特法则:每一个软件单位对其他单位只有最少的知识,而且局限于那些与基本单位密切相关的软件单位
软件实体应当尽可能少与其他实体之间发生相互作用。
工厂方法模式:定义一个用于创建对象的接口,但是让子类决定将哪一个类实例化。工厂方法模式让一个类的实例延迟到其子类
1、实例说明:
某系统运行日志记录器(Logger)可以通过多种途径保存系统的运行日志,例如通过文件记录或数据库记录,用户可以通过修改配置文件灵活地更换日志记录方式。在设计各类日志记录器时,开发人员发现需要对日志记录器进行一些初始化工作,初始化参数的设置过程较为复杂,而且某些参数的设置有严格的先后次序,否则可能会发生记录失败。
2、实例类图
3、示例代码体验:
(1)Logger:日志记录器接口,充当抽象产品接口。
public interface Logger {
public void wtriteLogger();
}
(2)DatabaseLogger:数据库日志记录器,充当具体产品角色
public class DatabaseLogger implements Logger{
@Override
public void wtriteLogger() {
System.out.println("数据库日志记录..............");
}
}
(3)FileLogger:文件日志记录器,充当具体产品角色
public class FileLogger implements Logger{
@Override
public void wtriteLogger() {
System.out.println("文件日志记录.............");
}
}
(4)LoggerFactory:日志记录器工厂接口,充当抽象工厂角色
public interface LoggerFactory {
public Logger createLogger();//抽象工厂方法
}
(5)DatabaseLoggerFactory:数据路日志记录器工厂类,充当具体工厂角色
public class DatabaseLoggerFactory implements LoggerFactory{
@Override
public Logger createLogger() {
//里氏代换原则
Logger logger=new DatabaseLogger();//可以用子类来代替父类,但是不能用子类来代替父类
return logger;
}
}
(6)FileLoggerFactory:文件日志记录器工厂类,充当具体工厂角色
public class FileLoggerFactory implements LoggerFactory{
@Override
public Logger createLogger() {
Logger logger=new FileLogger();
return logger;
}
}
(7)TestClient客户端测试类
public class TestClient {
public static void main(String[] args) {
LoggerFactory factory;
Logger logger;
factory=new FileLoggerFactory();
logger=factory.createLogger();
logger.wtriteLogger();
}
}
没有什么好说的、代码如下:
public interface LoggerFactory{
public Logger createLogger();
public Logger createLogger(String args);
public Logger createLogger(Object obj);
}
这个也没有啥,也就是把直接在工厂中调用所创建的产品对象中的业务方法。
public abstract class LoggerFactory(){
//在工厂方法中直接调用日志记录器类的业务方法writeLog()
public void writeLog(){
Logger logger=this.createLogger();
logger.writeLog();//就是这句
}
}
1、工厂方法模式的优点:
主要是提供了专门的工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节; 它能够让工厂自主确定创建何种产品对象,而如何创建这个对象的细节完全封装在具体工厂内部;在系统中加人新产品时完全符合开闭原则。
2、缺点:
主要是系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,会给系统带来一些 额外的开销;增加了系统的抽象性和理解难度。
3、工厂方法模式适用于以下环境:
客户端不知道它所需要的对象的类;抽象工厂类通过其子类来指定创建哪个对象。
抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,二无须指定它们具体的类
相对于工厂方法模式就是,在工厂方面更加细化了罢,抽象出来的东西多了,自然做出来的东西就多喽
产品族:比如一个产品族,海尔系列所有产品(海尔冰箱,海尔洗衣机,海尔电视)
产品结构:对于某一个产品族(电视机,冰箱,电视,空调),这些就是它的等级结构。
抽象工厂模式结构图:
抽象工厂模式包含的几个角色:
(1) AbstractFactory(抽象工厂):它声明了一组用于创建一族产 品的方法,每一个方法对应一种产品
(2)ConcreteFactory(具体工厂):它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。
(3)AbstractProduct(抽象产品):它为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法。
(4)ConereteProduct(具体产品):它定义具体工厂生产的具体产品对象,实现抽象产品接口中声明的业务方法。
1、实例说明:某软件公司要开发一套界面皮肤库,可以对基于Java的桌面软件进行界面美化。用户在使用时可以通过菜单来选择皮肤,不同的皮肤将提供视觉效果不同的按钮、文本框、组合框等界面元素,例如春天(Spring)风格的皮肤将提供浅绿色的按钮、绿色边框的文本框和绿色边框的组合框,而夏天(Summer)风格的皮肤则提供浅蓝色的按钮、蓝色边框的文本框和蓝色边框的组合框
2、实例类图:
3、示例代码体验:
(1)Button:按钮接口,充当抽象产品
public interface Button {
public void display();
}
(2)SpringButton:Spring按钮类,充当具体产品
public interface SkinFactory {
public Button createButton();
public TextField createTextField();
public ComboBox createComboBox();
}
(3)SummerButton:Summer按钮类,充当具体产品
public class SummerButton implements Button{
@Override
public void display() {
System.out.println("显示浅蓝色按钮");
}
}
(4)TextField:文本框接口,充当抽象产品
public interface TextField {
public void display();
}
(5)SpringTextField:Spring文本框,充当具体产品
public class SpringTextField implements TextField{
@Override
public void display() {
System.out.println("显示绿色文本边框");
}
}
(6)SummerTextField:Summer文本框类,充当具体产品
public class SummerTextField implements TextField{
@Override
public void display() {
System.out.println("显示蓝色边框文本框........");
}
}
(7)ComboBox:组合框接口,充当产品类
public interface ComboBox {
public void display();
}
(8)SpringComboBox:Sprin组合框类,充当具体产品类
public class SpringComboBox implements ComboBox{
@Override
public void display() {
System.out.println("显示绿色边框组合边框");
}
}
(9)SummerComboBox:Summer组合框类,充当具体产品
public class SummerComboBox implements ComboBox{
@Override
public void display() {
System.out.println("显示蓝色边框组合边框");
}
}
(10)SkinFactory:界面皮肤工厂接口,充当抽象工厂
public interface SkinFactory {
public Button createButton();
public TextField createTextField();
public ComboBox createComboBox();
}
(11)SpringSkinfactory:Spring皮肤工厂,充当具体工厂
public class SpringSkinFactory implements SkinFactory{
@Override
public Button createButton() {
return new SpringButton();
}
@Override
public ComboBox createComboBox() {
return new SpringComboBox();
}
@Override
public TextField createTextField() {
return new SpringTextField();
}
}
(12)SummerSkinfactory:Summer皮肤工厂,充当具体工厂
public class SummerSkinFactory implements SkinFactory{
@Override
public Button createButton() {
return new SummerButton();
}
@Override
public ComboBox createComboBox() {
return new SummerComboBox();
}
@Override
public TextField createTextField() {
return new SummerTextField();
}
}
(13)Client测试类
public class Client {
public static void main(String[] args) {
//使用抽象层定义
SkinFactory factory=new SpringSkinFactory();
Button bt=factory.createButton();
TextField tf=factory.createTextField();
ComboBox cb=factory.createComboBox();
bt.display();
tf.display();
cb.display();
}
}
抽象工厂模式无法解决此类问题,这也是抽象工厂模式的最大缺点所在。在抽象工厂模式增加新的产品族很方便,但是增加新的产品等级结构很麻烦,抽象工厂模式的这种性质称为开闭原则的倾斜性。
1、抽象工厂模式的优点:主要是隔离了具体类的生成,使得客户端并不需要知道什么被创建;当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同-一个产品族中的对象;增加新的产品族很方便,无须修改已有 系统,符合开闭原则。
2、缺点主要是增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,违背了开闭原则。
3、抽象工厂模式适用于以下环境:一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节;系统中有多于一个的产品族,而每次只使用其中某一产品族;属于同一个产品族的产品将在一起使用,这一 约東必须在系统的设计中体现出来;产品等级结构稳定,在设计完成之后不会向系统中增加新的产品等级结构或者删除e有的产品等级结构。
单例模式:确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例
为什么要设计单例模式,比如你电脑的任务管理器,只能有一个的吧,不然会引起异步不同的状态。
单例模式三个要点:
talk is cheap show you the code
public class Singleton {
private static Singleton instance=null;//静态私有成员变量
//私有构造函数
private Singleton(){
}
//静态公有工厂方法,返回唯一实例
public static Singleton getInstance(){
if(instance==null){
instance=new Singleton();
}
return instance;
}
}
测试类
public class Client {
public static void main(String[] args) {
Singleton s1=Singleton.getInstance();
Singleton s2=Singleton.getInstance();
//判断一下
System.out.println(s1==s2);
}
}
其输出结果:true
单例模式需要注意以下三点:
1、实例说明
某软件公司承接了一个服务器负载均衡(Load Balance)软件的开发工作,该软件运行在一台负载均衡服务器上,可以将并发访问和数据流量分发到服务器集群中的多台设备上进行并发处理,提高系统的整体处理能力,缩短响应时间。由于集群中的服务器需要动态删减,且客户端请求需要统一分发,因此需要确保负载均衡器的唯一性,只能有一个负载均衡器来负责服务器的管理和请求的分发,否则将会带来服务器状态的不一致以及请求分配冲突等问题。如何确保负载均衡器的唯一性是该软件成功的关键,试使用单例模式设计服务器负载均衡器;负载均衡器LoadBalancer设计为单例类,其中包含一个存储服务器信息的集合serverList,每次在serverList中随机选择台服务器来响应客户端的请求。
2、实例类图
3、实例代码
代码上例已经实例过了,不再进行实例了
饿汉式单例类在类破加载时就将自己实例化,它的优点在于无须考虑多个线程同时访问的问题,可以确保实例的唯一性;从调用速度和反应时间角度来讲,由于单例对象一开始就得以创建,因此要优于懒汉式单例。但是无论系统在运行时是否需要使用该单例对象,由于在类加载时该对象就需要创建,因此从资源利用效率角度来讲饿汉式单例不及懒汉式单例,而且在系统加载时由于需要创建饿汉式单例对象,加载时间可能会比较长。
public class EagerSingleton {
private static final EagerSingleton instance=new EagerSingleton();
private EagerSingleton(){
}
public static EagerSingleton getInstance(){
return instance;
}
}
饿汉式单例:当类被加载时,静态变量instance会被初始化,此时类的私有构造函数会被调用,单例类的唯一被创建
懒汉式单例类在第-次使用时创建,无须一直占用系统资源,实现了延迟加载,但是必须处理多个线程同时访问的问题,特别是当单例类作为资源控制器,在实例化时必然涉及资源初始化,而资源初始化很有可能耗费大量时间,这意味着出现多线程同时首次引用此类的几率变得较大,需要通过双重检查锁定等机制进行控制,这将导致系统性能受到一定影响。
public class LazySingleton {
private static LazySingleton instance=null;
//使用synchronized关键字对方法加锁,确保任意时刻只有一个线程可执行该方法
synchronized public static LazySingleton getInstance(){
if(instance==null){
instance=new LazySingleton();
}
return instance;
}
}
懒汉式单例的构造函数也是私有的。不同的是,懒汉式单例在第一次被引用时将自己实例化,在懒汉式单例类被加载时不会将自己实例化。在懒汉单例类中,通过在getInstance()方法前加了sychronized进行了线程锁定,来处理多个线程的访问问题。
对于懒汉式单例,线程等待执行,创建出实例后,第二个线程并不知道线程是否已经被创建,所以再次进行一次"instance==null"来判断,我们称之为“双重检查锁定”
public class LazySingleton1 {
private volatile static LazySingleton1 instance=null;
private LazySingleton1(){}
public static LazySingleton1 getInstance(){
//第一重判断
if(instance==null){
synchronized (LazySingleton1.class){
//第二重判断
if(instance==null){
instance=new LazySingleton1();//创建单例实例
}
}
}
return instance;
}
}
1、单例模式的优点主要在于提供了对唯一实例的受控访问并可以节约系统资源
2、缺点主要在于因为缺少抽象层而难以扩展,且单例类职责过重,将太多功能耦合在起。
3、单例模式适用于以下环境:系统只需要一个实例对象;客户调用类的单个实例只允许使用一个公共访问点。
4、饿汉式单例在类加载的时候创建唯-实例,懒汉式单例在第- 次调用静态工厂方法时创建唯一实 例。
5、懒汉式单例类中为了确保线程安全,避免创建多 个单例对象.需婴使用双重检在锁定机制对单例对象的创建进行控制。
适配器模式:将一个类的接口转换成为客户希望的另外一个接口。适配器模式让那些接口不兼容的类可以一起工作
适配器模式包括类适配器和对象适配器。在对象适配器模式中,适配器与适配者之间是关联关系;在类适配器模式中,适配器与是适配者之间是继承(或实现)关系,说到这里,你一定得想到大多数使用的是对象适配器模式,因为java语言不支持多继承。
适配器中几个角色:
(1)Target(目标抽象类):目标抽象类定义客户所需的接口,可以是一个抽象类或接口,也可以是具体类。
(2)Adapter(适配器类):它可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配。适配器Adapter 是适配器模式的核心,在类适配器中,它通过实现Target接口并继承Adaptee类来使二者产生联系,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。
(3)Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下甚至没有适配者类的源代码。
1、实例说明:某公司要开发一款儿童玩具汽车,为了更好地吸引小朋友的注意力,该玩具汽车在移动过程中伴随着灯光闪烁和声音提示。在该公司以往的产品中已经实现了控制灯光闪烁(例如警灯闪烁)和声音提示(例如警笛音效)的程序,为了重用先前的代码并且使汽车控制软件具有更好的灵活性和扩展性,现使用适配器模式设计该玩具汽车控制软件。
2、实例类图:
3、示例代码体验:
(1)CarController:汽车控制类,充当目标抽象类
public abstract class CarController {
public void move(){
System.out.println("玩具汽车移动............");
}
public abstract void phonate();//发出声音
public abstract void twinkle();//灯光闪烁
}
(2)PoliceSound:警笛类,充当适配者类
public class PoliceSound {
public void alarmSound(){
System.out.println("发出警笛..........");
}
}
(3)PoliceLamp:警灯类,充当适配者类
public class PoliceLamp {
public void alarmLamp(){
System.out.println("呈现警灯闪烁............");
}
}
(4)PoliceCarAdapter:警车适配器,充当适配器
public class PoliceCarAdapter extends CarController{
private PoliceSound sound;//定义适配者PoliceSound对象
private PoliceLamp lamp;//定义适配者PoliceLamp对象
//发出警笛
@Override
public void phonate() {
sound.alarmSound();//调用适配者类PoliceSound方法
}
//呈现警灯
@Override
public void twinkle() {
lamp.alarmLamp();//调用适配者类PoliceLamp方法
}
}
(5)Client测试类:
public class Client {
public static void main(String[] args) {
CarController carController=new PoliceCarAdapter();
carController.move();
carController.phonate();
carController.twinkle();
}
}
适配器模式的优点:
主要是将目标类和适配者类解耦,通过引人一个适配器类来重用现有的适配者类,无须修改原有结构;增加了类的透明性和复用性,并且让系统的灵活性和扩展性都非常好。此外,在类适配器模式中置换一- 些适配者的方法很方便;通过对象适配器模式可以把多个不同的适配者适配到同一个目标,还可以适配一个适配者的子类。
类适配器模式的缺点:
主要包括一次最多 只能适配一个适配者类,不能同时适配多个适配者;适配者类不能为最终类且类适配器模式中的目标抽象类只能为接口,不能为类。对象适配器模式的缺点主要是要在适配器中置换适配者类的某些方法比较麻烦。
适配器模式适用于以下环境:
系统需要使用一些现有的类,而这些类的接口不符合系统的需要.甚至没有这此类的源代码;想创建- 个可以重复使用的类,用于和一些彼此之间没有太大关联的类(包括一些可能在将来引进的类)一起工作。
桥接模式:将抽象部分与它的实现部分解耦,使得两者都能够独立变化
桥接模式结构图
比如用彩笔画画,彩笔有粗壮大小和颜色两个属性,当然这两个属性有很多不同的值,如2B 2C;红色,蓝色,也就是可以把这两个属性看成两个维度。所谓桥接模式,就是把颜色扔出去(这里的扔指的是还会与抽象的颜色之间有关联关系),把颜色看做是一个抽象类,通过保存有粗壮大小的实例来实现颜色就行了。
1、实例说明:
某软件公司要开发一个跨平台图像浏览系统,要求该系统能够显示BMP、JPG、GIF、PNG等多种格式的文件,并且能够在Windows、Linux、UNIX等多个操作系统上运行。系统首先将各种格式的文件解析为像素矩阵(Matrix),然后将像素矩阵显示在屏幕上,在不同的操作系统中可以调用不同的绘制函数来绘制像素矩阵。系统需具有较好的扩展性,以便在将来支持新的文件格式和操作系统。
2、实例类图:
3、实例代码:
(1)Matrix:像素矩阵类,它是一个辅助类,各种格式的文件最终都被转化为像素矩阵,不同的操作系统提供不同的方式显示像素矩阵
public class Matrix {
//代码省略
}
```
(2)ImageImpl:抽象操作系统实现类,充当实现接口
```java
public interface ImageImp {
public void doPaint(Matrix m);//显示像素矩阵m
}
```
(3)WindowsImpl:WindowsImpl操作系统实现类,充当具体实现类
```java
public class WindowsImpl implements ImageImp{
@Override
public void doPaint(Matrix m) {
//调用Windows系统的绘制函数绘制像素矩阵
System.out.println("在Windows操作系统中显示图像");
}
}
(4)LinuxImpl:LinuxImpl操作系统实现类,充当具体实现类
public class LinuxImpl implements ImageImp{
@Override
public void doPaint(Matrix m) {
//调用Linux系统的绘制函数绘制像素矩阵
System.out.println("在Linux操作系统中显示图像");
}
}
(5)UnixImpl:UnixImpl操作系统实现类,充当具体实现类
public class UnixImpl implements ImageImp{
@Override
public void doPaint(Matrix m) {
//调用Unix系统的绘制函数绘制像素矩阵
System.out.println("在Unix操作系统中显示图像");
}
}
(6)Image:抽象图像类,充当抽象类
public abstract class Image {
protected ImageImp imp;
//注入实现类接口对象
public void seImageImp(ImageImp imp){
this.imp=imp;
}
public abstract void parseFile(String fileName);
}
(7)JPGImage:JPG格式图像类,充当扩充抽象类
public class JPGImage extends Image{
@Override
public void parseFile(String fileName) {
//模拟解析JPG文件并获得一个像素矩阵对象m
Matrix m=new Matrix();
imp.doPaint(m);
System.out.println(fileName+",格式为JPG");
}
}
(8)BMPImage:BMP格式图像类,充当扩充抽象类
public class BMPImage extends Image{
@Override
public void parseFile(String fileName) {
//模拟解析BMP文件并获得一个像素矩阵对象m
Matrix m=new Matrix();
imp.doPaint(m);
System.out.println(fileName+",格式为BMP");
}
}
(9)GIFImage:GIF格式图像类,充当扩充抽象类
public class GIFImage extends Image{
@Override
public void parseFile(String fileName) {
//模拟解析GIF文件并获得一个像素矩阵对象m
Matrix m=new Matrix();
imp.doPaint(m);
System.out.println(fileName+",格式为GIF");
}
}
(10)PNGImage:PNG格式图像类,充当扩充抽象类
public class PNGImage extends Image{
@Override
public void parseFile(String fileName) {
//模拟解析PNG文件并获得一个像素矩阵对象m
Matrix m=new Matrix();
imp.doPaint(m);
System.out.println(fileName+",格式为PNG");
}
}
(11)Client测试类:
public class Client {
public static void main(String[] args) {
Image image;
ImageImp imp;
image=new GIFImage();
imp=new UnixImpl();
image.seImageImp(imp);
image.parseFile("小龙女呀");
}
}
桥接模式的优点:
主要在于可以分离抽象接口及其实现部分,它是比多层继承方案市好的解决方法极大地减少了子类的个数,此外桥接模式提高了系统的可扩展性,在两个变化维度中任意扩展一个维度都不需要修改原有系统,符合开闭原则。
缺点:主要在于会增加系统的理解与设计难度,且正确识别出系统中的两个独立变化的维度并不是件容 易的事情。
桥接模式适用于以下环境:
需要在抽象化和具体化之间增加更多的灵活性,避免在两个层次之间建立静态的继承关系;抽象部分和实现部分可以用继承的方式独立扩展而互不影响;一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展;不希望使用继承或因为多层继承导致系统类的个数急剧增加的系统。
组合模式:组合多个对象形成树形结构以表示具有部分-整体关系的层次结构。组合模式让客户端可以统一对待单个对象和组合
1、组合模式的角色:
(1) Component(抽象构件):它可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。
(2) Leaf(叶子构件):它在组合结构中表示叶子结点对象,叶子结点没有子结点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过抛出异常、提示错误等方式进行处理。
(3) Composite(容器构件):它在组合结构中表示容器结点对象,容器结点包含子结点,其子结点可以是叶子结点,也可以是容器结点,它提供一个集合用于存储子结点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子结点的业务方法。
1、应用实例:
某软件公司要开发一个杀毒( Antivirus)软件,该软件既可以对某个文件夹(Folder)杀毒,也可以对某个指定的文件(File)杀毒。该杀毒软件还可以根据各类文件的特点为不同类型的文件提供不同的杀毒方式,例如图像文件(ImageFile)和文本文件(TextFile)的杀毒方式就有所差异。现使用组合模式来设计该杀毒软件的整体框架。
2、实例代码:
(1)AbstractFile:抽象文件类,充当抽象构建类
public abstract class AbstractFile {
public abstract void add(AbstractFile file);
public abstract void remove(AbstractFile file);
public abstract AbstractFile getChild(int i);
public abstract void killVirus();
}
(2)ImageFile:图像文件类,充当叶子构建类
public class ImageFile extends AbstractFile{
private String name;
public ImageFile(String name){
this.name=name;
}
public void add(AbstractFile file){
System.out.println("对不起,不支持该方法");
}
public void remove(AbstractFile file){
System.out.println("对不起,不支持该方法");
}
public AbstractFile getChild(int i){
System.out.println("对不起,不支持该方法");
return null;
}
public void killVirus(){
System.out.println("----对图像文件 "+name+" 进行杀毒");
}
}
(3)TextFile:文本文件类,充当叶子构建类
public class TextFile extends AbstractFile{
private String name;
public TextFile(String name){
this.name=name;
}
public void add(AbstractFile file){
System.out.println("对不起,不支持该方法");
}
public void remove(AbstractFile file){
System.out.println("对不起,不支持该方法");
}
public AbstractFile getChild(int i){
System.out.println("对不起,不支持该方法");
return null;
}
public void killVirus(){
System.out.println("----对图像文件 "+name+" 进行杀毒");
}
}
(4)VideoFile:视频文件类,充当叶子构建类
public class VideoFile extends AbstractFile{
private String name;
public VideoFile(String name){
this.name=name;
}
public void add(AbstractFile file){
System.out.println("对不起,不支持该方法");
}
public void remove(AbstractFile file){
System.out.println("对不起,不支持该方法");
}
public AbstractFile getChild(int i){
System.out.println("对不起,不支持该方法");
return null;
}
public void killVirus(){
System.out.println("----对图像文件 "+name+" 进行杀毒");
}
}
(5)Folder:文件夹类,充当容器构建类
public class Folder extends AbstractFile{
//定义集合filelist,用于存储AbstractFile类型成员
private ArrayList<AbstractFile> filelist=new ArrayList<>();
private String name;
Folder(String name){this.name=name;}
@Override
public void add(AbstractFile file) {
filelist.add(file);
}
@Override
public void remove(AbstractFile file) {
filelist.remove(file);
}
@Override
public AbstractFile getChild(int i) {
return (AbstractFile) filelist.get(i);
}
@Override
public void killVirus() {
System.out.println("****对文件夹 "+name+" 进行杀毒");//模拟杀毒
//递归调用成员构件的killVirus()方法
for(Object obj:filelist){
((AbstractFile)obj).killVirus();
}
}
}
(6)Client:测试类
public class Client {
public static void main(String[] args) {
AbstractFile file1,file2,file3,file4,file5,folder1,folder2,folder3,folder4;
folder1=new Folder("Sunny的写真资料");
folder2=new Folder("图像文件");
folder3=new Folder("文本文件");
folder4=new Folder("视频文件");
file1=new ImageFile("苍井空.jpg");
file2=new ImageFile("苍井空的男朋友.gif");
file3=new TextFile("宅男计划");
file4=new TextFile("少女养成计划");
file5=new TextFile("离间计");
folder2.add(file1);
folder2.add(file2);
folder3.add(file3);
folder3.add(file4);
folder4.add(file5);
folder1.add(folder2);
folder1.add(folder3);
folder1.add(folder4);
//从Sunny的资料结点开始进行杀毒操作
folder1.killVirus();
}
}
组合模式的优点:主要在于可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制;客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码;增加新的容器构件和叶子构件都很方便。
**缺点:**主要是在增加新构件时很难对容器中的构件类型进行限制。
**组合模式适用于以下环境:**在具有整体和部分的层次结构中希望通过种方式忽略整体与部分的差异,客户端可以一致地对待它们;在一- 个使用面向对象语言开发的系统中需要处理个树形结构;在-.个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型。
装饰模式:动态的给一个对象增加一些额外的职责。就扩展功能而言,装饰模式提供了一种比使用子类更加灵活的替代方案
装饰模式是一种结构型模式,它以对客户端透明的方式动态的给一个对象附加上更多的责任,可以在不需要创建更多子类的情况下让对象的功能得以扩展。
装饰模式结构图:
装饰模式的角色:
(1)**Component(抽象构件)?*它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引人可以使客户端以一 致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。
(2) **ConcreteComponent(具体构件)?*它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰类可以给它增加额外的职责(方法)。
(3)**Decorator( 抽象装饰类)?*它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。
(4)**ConcreteDecorator(具体装饰类)?*它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一此新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用于扩充对象的行为。
装饰模式结构图:
1、应用实例:
某软件公司基于面向对象技术开发了一套图形界面构件库VisualComponent,该构件库提供了大量的基本构件,如窗体、文本框、列表框等,由于在使用该构件库时用户经常要求定制一些特殊的显示效果,如带滚动条的窗体、带黑色边框的文本框、既带滚动条又带黑色边框的列表框等,因此经常需要对该构件库进行扩展以增强其功能。
现使用装饰模式来设计该图形界面构件库。
2、代码示例:
(1)Component:抽象界面构建类,充当抽象构建类,为了突出于模式相关核心代码,在本实例中对控件代码进行了大量的简化。
public abstract class Component {
public abstract void display();
}
(2)Window:窗体类,充当具体构建类
public class Window extends Component{
@Override
public void display() {
System.out.println("显示窗体..........");
}
}
(3)TextBox:文本框类,充当具体构建类
public class TextBox extends Component{
@Override
public void display() {
System.out.println("显示文本框...............");
}
}
(4)ListBox:列表框类,充当具体构建类
public class ListBox extends Component{
@Override
public void display() {
System.out.println("显示列表框...............");
}
}
(5)ComponentDecorator:构建装饰类,充当抽象装饰类
public class ComponentDecorator extends Component{
private Component component;
public ComponentDecorator(Component component){
this.component=component;
}
@Override
public void display() {
component.display();
}
}
(6)ScrollBarDecorator:滚动条装饰类,充当具体装饰类
public class ScrollBarDecorator extends ComponentDecorator{
public ScrollBarDecorator(Component component){
super(component);
}
public void display(){
this.setScrollBar();
super.display();
}
public void setScrollBar(){
System.out.println("构建增加滚动条...........");
}
}
(7)BlackBorderDecorator:黑色边框装饰类,充当具体装饰类
public class BlackBorderDecorator extends ComponentDecorator{
public BlackBorderDecorator(Component component){
super(component);
}
public void display(){
this.setBlackBorder();
super.display();
}
public void setBlackBorder(){
System.out.println("构建增加褐色边框...........");
}
}
(8)Client:测试类
public class Client {
public static void main(String[] args) {
Component component,componentSB;//使用抽象构建定义对象
component=new Window();//创建具体构建对象
componentSB=new BlackBorderDecorator(component);//创建装饰后的构建对象
componentSB.display();
}
}
**装饰模式的优点:**主要是在扩展功能时比继承更加灵活,不会导致类的个数急剧增加;它通过-种动态的方式来扩展一-个对象的功能,可以对-个对象进行多次装饰,还通过使用不同的具体装饰类以及这些装饰类的排列组合创造出很多不同行为的组合,得到功能更加强大的对象;具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合开闭原则。
**缺点:**主要是使用装饰模式进行系统设计时将产生很多小对象;此外,装饰模式比继承更加易于出错,排错也更困难,对于多次装饰的对象,在调试时寻找错误可能需要逐级排查.较为烦琐。
**装饰模式适用于以下环境:**在不影响其他对象的情况下以动态、透明的方式给单个对象添加职责;当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时也可以使用装饰模式。
观察者模式:定义对象之间的一种一对多的依赖关系,使得每当一个对象状态发生改变时其相关的依赖对象皆得到通知并被自动更新
1、实例说明:
在某个多人联机对战游戏中,多个玩家可以加入同一个战队组成联盟,当战队中的某一个成员受到敌人攻击时将给所有其他盟友发送通知,盟友收到t通知后将作出响应,下面通过观察者模式来实现战队成员之间的联动
2、实例类图:
3、实例代码:
(1)AllyControlCenter:指挥部(战队控制中心)类,充当抽象目标类
public abstract class AllyControlCenter {
protected String allyName;//战队名称
protected ArrayList<Observer> players=new ArrayList<>();//定义一个集合用于存储战队成员
public String getAllyName() {
return allyName;
}
public void setAllyName(String allyName) {
this.allyName = allyName;
}
//注册方法
public void join(Observer obs){
System.out.println(obs.getName()+"加入 "+this.allyName+"战队!");
players.add(obs);
}
//注销方法
public void quit(Observer obs){
System.out.println(obs.getName()+"退出 "+this.allyName+"战队!");
players.remove(obs);
}
//声明抽象通知方法
public abstract void notifyObserver(String name);
}
(2)ConcreteAllyControlCenter:具体指挥部类,充当具体目标类
public class ConcreteAllyControlCenter extends AllyControlCenter{
public ConcreteAllyControlCenter(String allyName){
System.out.println(allyName+"战队组件成功!");
System.out.println("-------------------------------");
this.allyName=allyName;
}
//实现通知方法
@Override
public void notifyObserver(String name) {
System.out.println(this.allyName+"战队紧急通知,盟友"+name+"遭受敌人攻击!");
//遍历观察者集合调用每一个盟友(自己除外)的支援方法
for(Observer obs:players){
if(!((Observer)obs).getName().equalsIgnoreCase(name)){
((Observer)obs).help();
}
}
}
}
(3)Observer::抽象观察者类
public interface Observer {
public String getName();
public void setName(String name);
public void help(); //声明支援盟友方法
public void beAttacked(AllyControlCenter acc);//声明遭受攻击方法
}
(4)Player:战队成员类,充当具体观察者类
public class Player implements Observer{
/**
* 战队成员,充当具体观察者类
*/
private String name;
public Player(String name){
this.name=name;
}
public void setName(String name){
this.name=name;
}
public String getName(){
return this.name;
}
//支援盟友方法的实现
@Override
public void help() {
System.out.println("坚持住,"+this.name+"来救你!");
}
//遭受攻击方法的实现,当遭受攻击时将调用战队控制中心类的通知方法notifyObserver()来通知盟友
@Override
public void beAttacked(AllyControlCenter acc) {
System.out.println(this.name+"被攻击!");
//类似于同步
acc.notifyObserver(name);
}
}
(5)Client:客户端类
public class Client {
public static void main(String[] args) {
//定义观察目标对象
AllyControlCenter acc;
acc=new ConcreteAllyControlCenter("金庸群侠");
//定义四个观察者对象
Observer players1,players2,players3,players4;
players1=new Player("杨过");
acc.join(players1);
players2=new Player("令狐冲");
acc.join(players2);
players3=new Player("张无忌");
acc.join(players3);
players4=new Player("杨过");
acc.join(players4);
//某成员遭受攻击
players1.beAttacked(acc);
}
}
注:有的模式没有写全,理解不够深,加上自己也不经常用,就没有写,通过以后的学习,在慢慢体会刘伟老师的设计模式。