设计模式-结构性设计模式
外观模式
-
定义
又叫门面模式,提供一个统一的接口,用来访问子系统的一群接口
外观模式定义了一个高层接口,让子系统更容易使用 -
适用场景
子系统越来越复杂,增加外观模式提供简单调用接口
构建多层系统结构,利用外观对象作为每层的入口,简化层间调用 -
优点
简化了调用过程,无需了解深入子系统,防止带来风险
减少系统依赖、松散耦合
更好的划分访问层次
符合迪米特原则,即最少知道原则
符合合成复用原则 -
缺点
增加子系统、扩展子系统行为容易引入风险
不符合开闭原则
积分兑换服务依赖3个子系统:分别是积分-库存校验服务、积分扣除服务、物流对接服务。主系统只提供一个简单调用的接口,应用层Test不关心子系统的运作
适配器模式
-
定义
将一个类的接口转换成客户期望的另一个接口
使原本接口不兼容的类可以一起工作 -
适用场景
已经存在的类,它的方法和需求不匹配时(方法结果相同或相似)
不是软件设计阶段考虑的设计模式,而是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案 -
优点
能提高类的透明性和复用,现有的类复用但不需要改变
目标类和适配器类解耦,提高程序扩展性
符合开闭原则 -
缺点
适配器编写过程需要全面考虑,可能会增加系统的复杂性
增加系统代码可读的难度 -
类适配器
/**目标接口
* @author MrPeng
* @date 2019/2/14
*/
public interface Target {
void request();
}
------------------------
/**目标实现类
* @author MrPeng
* @date 2019/2/14
*/
public class ImpTarget implements Target {
@Override
public void request() {
System.out.println("ImpTarget目标实现方法");
}
}
----------------------------
/**被适配者
* @author MrPeng
* @date 2019/2/14
*/
public class Adaptee {
public void adapteeRequest(){
System.out.println("被适配者的方法");
}
}
-----------------------------------------
/**类适配器--通过继承重写被适配者的方法
* @author MrPeng
* @date 2019/2/14
*/
public class Adapter extends Adaptee implements Target {
@Override
public void request() {
//重写的逻辑代码...
super.adapteeRequest();
//重写的逻辑代码...
}
}
-------------------------------------------
/**适配器模式测试
* @author MrPeng
* @date 2019/2/14
*/
public class Test {
public static void main(String[] args) {
Target target = new ImpTarget();
target.request();
Target adapterTarget = new Adapter();
adapterTarget.request();
}
}
- 对象适配器
/**对象适配器--不使用继承,而是通过组合
* @author MrPeng
* @date 2019/2/14
*/
public class Adapter implements Target {
private Adaptee adaptee = new Adaptee();
@Override
public void request() {
//重写的逻辑代码...
adaptee.adapteeRequest();
//重写的逻辑代码...
}
}
桥接模式
- 定义
将抽象部分与它的具体实现部分分离,使它们都可以独立变化
通过组合的方式建立两个类之间的联系,而不是继承 - 适用场景
抽象和具体实现之间增加更多灵活性
一个类存在两个或多个独立变化的维度,且这些维度都需要独立进行扩展
不希望使用继承,或因为多层继承导致系统类的个数剧增 - 优点
分离抽象部分和其具体实现部分
提高了系统的可扩展性
符合开闭原则
符合合成复用原则 - 缺点
增加了系统的理解和设计难度
需要正确地识别出系统中两个独立变化的维度
/**帐号接口
* @author MrPeng
* @date 2019/2/16
*/
public interface Account {
Account openAccount();
void showAccountType();
}
-----------------------------
/**定期帐号--实现帐号接口
* @author MrPeng
* @date 2019/2/16
*/
public class DepositAccount implements Account {
@Override
public Account openAccount() {
System.out.println("打开定期账号");
return new DepositAccount();
}
@Override
public void showAccountType() {
System.out.println("这是一个定期账号");
}
}
-------------------------------------
/**银行抽象
* @author MrPeng
* @date 2019/2/16
*/
public abstract class Bank {
protected Account account;
public Bank(Account account){
this.account = account;
}
abstract Account openAccount();
}
---------------------------------
/**工商银行
* @author MrPeng
* @date 2019/2/16
*/
public class ICBCBank extends Bank {
public ICBCBank(Account account) {
super(account);
}
@Override
Account openAccount() {
System.out.println("打开中国工商银行账号");
//关键部分
account.openAccount();
return account;
}
}
---------------------
public static void main(String[] args) {
Bank icbcBank = new ICBCBank(new DepositAccount());
Account icbcAccount = icbcBank.openAccount();
icbcAccount.showAccountType();
}
组合模式
- 定义
将对象组合成树形结构以表示“部分-整体”的层次结构
组合模式使客户端对单个对象和组合对象保持一致的方式处理 - 适用场景
希望客户端可以忽略组合对象与单个对象的差异时
处理一个树形结构时 - 优点
清楚地定义分层次和复杂对象,表示对象的全部或部分层次
让客户端忽略了层次的差异,方便对整个层次结构进行控制
简化客户端代码
符合开闭原则 - 缺点
限制类型时会较为复杂
使设计变得更加抽象
测试场景:无限极分类
/**目录组件
* @author MrPeng
* @date 2019/2/14
*/
public abstract class CatalogComponent {
public void add(CatalogComponent catalogComponent){
throw new UnsupportedOperationException("不支持添加操作");
}
public void remove(CatalogComponent catalogComponent){
throw new UnsupportedOperationException("不支持删除操作");
}
public String getName(CatalogComponent catalogComponent){
throw new UnsupportedOperationException("不支持获取名称操作");
}
public double getPrice(CatalogComponent catalogComponent){
throw new UnsupportedOperationException("不支持获取价格操作");
}
public void print(){
throw new UnsupportedOperationException("不支持打印操作");
}
}
---------------------------------------------
/**课程
* @author MrPeng
* @date 2019/2/14
*/
public class Course extends CatalogComponent {
private String name;
private double price;
public Course(String name, double price) {
this.name = name;
this.price = price;
}
@Override
public String getName(CatalogComponent catalogComponent) {
return this.name;
}
@Override
public double getPrice(CatalogComponent catalogComponent) {
return this.price;
}
@Override
public void print() {
System.out.println("Course Name:"+name+" Price:"+price);
}
}
-------------------------------------
**课程目录
* @author MrPeng
* @date 2019/2/14
*/
public class CourseCatalog extends CatalogComponent {
private List<CatalogComponent> items = new ArrayList<CatalogComponent>();
private String name;//目录名称
private Integer level;//目录等级
public CourseCatalog(String name,Integer level) {
this.name = name;
this.level = level;
}
@Override
public void add(CatalogComponent catalogComponent) {
items.add(catalogComponent);
}
@Override
public String getName(CatalogComponent catalogComponent) {
return this.name;
}
@Override
public void remove(CatalogComponent catalogComponent) {
items.remove(catalogComponent);
}
@Override
public void print() {
System.out.println(this.name);
for(CatalogComponent catalogComponent : items){
if(this.level != null){
for(int i = 0; i < this.level; i++){
System.out.print(" ");
}
}
catalogComponent.print();
}
}
}
-------------------------------------
public class Test {
public static void main(String[] args) {
CatalogComponent javaCourseCatalog = new CourseCatalog("Java课程目录",2);
CatalogComponent course1 = new Course("Java零基础入门",55);
CatalogComponent course2 = new Course("JavaWeb入门",66);
javaCourseCatalog.add(course1);
javaCourseCatalog.add(course2);
CatalogComponent phpCourseCatalog = new CourseCatalog("PHP课程目录",2);
CatalogComponent course3 = new Course("PHP零基础入门",55);
CatalogComponent course4 = new Course("PHP源码解析",66);
phpCourseCatalog.add(course3);
phpCourseCatalog.add(course4);
CatalogComponent mainCatalog = new CourseCatalog("主目录",1);
mainCatalog.add(javaCourseCatalog);
mainCatalog.add(phpCourseCatalog);
mainCatalog.print();
}
}
装饰器模式
- 定义
在不改变原有对象的基础上,将功能附加到对象上
提供了比继承更有弹性的替代方案(扩展原有对象功能) - 适用场景
扩展一个类的功能或给一个类添加附加职责
动态的给一个对象添加功能、这些功能可以动态的撤销 - 优点
继承的有利补充、比继承灵活、不改变原有对象的情况下给一个对象扩展功能
通过使用不同的装饰类以及这些装饰类的排列组合,可以实现不同效果
符合开闭原则 - 缺点
会出现更多的代码、更多的类、增加程序复杂性
动态装饰时,多层装饰时会更复杂
//创建一个普通煎饼
BaseCake baseCake = new Cake();
baseCake = new EggDecorator(baseCake);
baseCake = new EggDecorator(baseCake);
baseCake = new HotDogDecorator(baseCake);
System.out.println(baseCake.getDesc()+" 销售价格:"+baseCake.getPrice());
享元模式
- 定义
提供了减少对象数量从而改善应用所需的对象结构方式
运用共享技术有效的支持大量细粒度的对象 - 适用场景
常常应用于系统底层的开发,以便解决系统的性能问题
系统有大量对象,需要缓冲池的场景 - 优点
减少对象的创建,降低内存中对象的数量,降低系统的内存,提高效率
减少内存之外的其他资源占用(例如new对象的时间开销) - 缺点
关注内/外状态、线程安全问题
使系统、程序的逻辑复杂化 - 扩展
内部状态
外部状态
/**员工接口
* @author MrPeng
* @date 2019/2/14
*/
public interface Employee {
void report();
}
------------------------
/**部门经理
* @author MrPeng
* @date 2019/2/14
*/
public class Manager implements Employee {
@Override
public void report() {
System.out.println(reportContent);
}
/**
* 内部状态--外部无法改变
*/
private String title = "部门经理";
/**
* 外部状态--传入部门不同导致部门经理也不同
*/
private String department;
private String reportContent;
public void setReportContent(String reportContent) {
this.reportContent = reportContent;
}
public Manager(String department) {
this.department = department;
}
}
--------------------------------------
/**工厂类
* @author MrPeng
* @date 2019/2/14
*/
public class EmployeeFactory {
private static final Map<String,Employee> EMPLOYEE_MAP = new HashMap<>();
public static Employee getManager(String department){
Manager manager = (Manager) EMPLOYEE_MAP.get(department);
if(manager == null){
manager = new Manager(department);
System.out.print("创建部门经理:"+department);
String reportContent = department+"部门汇报:此次报告的主要内容是......";
manager.setReportContent(reportContent);
System.out.println(" 创建报告:"+reportContent);
EMPLOYEE_MAP.put(department,manager);
}
return manager;
}
}
------------------------------
public class Test {
private static final String departments[] = {"销售部","研发部","人事部"};
public static void main(String[] args) {
for(int i=0; i<10; i++){
String department = departments[(int)(Math.random() * departments.length)];
Manager manager = (Manager) EmployeeFactory.getManager(department);
manager.report();
}
}
}
代理模式
- 定义
为其他对象提供一种代理以控制对这个对象的访问
代理对象在客户端和目标对象之间起中介作用 - 适用场景
保护目标对象
增强目标对象 - 优点
职责清晰
代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。
降低系统耦合,扩展性好 - 缺点
代理模式会造成系统设计中类数目增加
在客户端和目标对象中增加代理对象,会降低处理速度
增加系统复杂度 - 扩展
静态代理
动态代理--JDK--java.lang.reflect.InvocationHandler接口
CGLib代理--CGLIB(Code Generation Library)是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。
/**被追求者类
* @author MrPeng
* @date 2019/2/18
*/
public class Girl {
private String name;
//get/set忽略
}
------------------------------------
/**送礼物接口
* @author MrPeng
* @date 2019/2/18
*/
public interface GiveGift {
void giveDolls();
}
-------------------------------------
/**追求者类
* @author MrPeng
* @date 2019/2/18
*/
public class Pursuit implements GiveGift{
private Girl mm;
public Pursuit(Girl girl) {
this.mm = girl;
}
@Override
public void giveDolls() {
System.out.println(mm.getName()+" 送娃娃");
}
}
----------------------------------------------------
/**代理类
* @author MrPeng
* @date 2019/2/18
*/
public class Proxy implements GiveGift {
private Pursuit gg;
public Proxy(Girl girl){
gg = new Pursuit(girl);
}
@Override
public void giveDolls() {
//可以通过代理增强类的功能--而不需要改变原来的逻辑
System.out.print("通过代理向--");
gg.giveDolls();
System.out.println("送完娃娃看电影...");
}
}
--------------------------------------------
public static void main(String[] args) {
Girl girl = new Girl();
girl.setName("小红");
Proxy proxy = new Proxy(girl);
proxy.giveDolls();
}