设计模式中属于创建模式的代理模式、适配器模式、装饰模式、桥接模式、外观模式、享元模式、组合模式
《设计模式之禅》笔记
类结构型模式:关心类的组合,由多个类可以组合成一个更大的系统,在类结构型模式中一般只存在继承关系和实现关系
对象结构型模式:关心类与对象的组合,通过关联关系使得在一个类中定义另一个类的实例对象,然后通过该对象调用其方法。更符合“合成复用原则”
代理模式
思想:为其他对象提供一种代理以控制对这个对象的访问。
例子:每一个著名的运动员都有自己的代理人,你如果相联系这个运动员你需要通过代理人来联系他。
/***
* 运动员抽象接口
*/
public interface IRunner {
//运动员的主要工作就是跑步
public void run();
}
/***
* 运动员实现类
*/
public class Runner implements IRunner {
public void run() {
System.out.println("运动员跑步:动作很潇洒");
}
}
/***
* 运动员代理类
*/
public class RunnerAgent implements IRunner {
private IRunner runner;
public RunnerAgent(IRunner _runner){
this.runner = _runner;
}
//代理人是不会跑的
public void run() {
Random rand = new Random();
if(rand.nextBoolean()){
System.out.println("代理人同意安排运动员跑步");
runner.run();
}else{
System.out.println("代理人心情不好,不安排运动员跑步");
}
}
}
/***
* 客户端代码
*/
public class Client {
public static void main(String[] args) {
//定义一个短跑运动员
IRunner liu = new Runner();
//定义liu的代理人
IRunner agent = new RunnerAgent(liu);
//要求运动员跑步
System.out.println("====客人找到运动员的代理要求其去跑步===");
agent.run();
}
}
- 代理模式优点:
1.职责清晰,业务对象本身只关注自己的事情不管其他事情,如果有非业务对象本身职责的事情交给代理实现既可以了
2.高扩展,不关注业务对象的具体实现做好代理,业务对象怎么变化都可以
3.智能化,cglib和jdk动态代理,任何类或接口都可以加代理 - 使用场景:
把源对象交给代理对象,让代理对象控制对象的访问。例如权限控制、事务处理等。
装饰模式
思想:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。
例子:上面的例子、一个运动员给他加上装饰。
/***
* 运动员抽象接口
*/
public interface IRunner {
//运动员的主要工作就是跑步
public void run();
}
/***
* 运动员实现类
*/
public class Runner implements IRunner {
public void run() {
System.out.println("运动员跑步:动作很潇洒");
}
}
/***
* 运动员装饰类
*/
public class RunnerWithJet implements IRunner {
private IRunner runner;
public RunnerWithJet(IRunner _runner){
this.runner = _runner;
}
public void run() {
System.out.println("加快运动员的速度:为运动员增加喷气装置");
runner.run();
}
}
/***
* 客户端代码
*/
public class Client {
public static void main(String[] args) {
//定义运动员
IRunner liu = new Runner();
//对其功能加强
liu = new RunnerWithJet(liu);
//看看它的跑步情况如何
System.out.println("===增强后的运动员的功能===");
liu.run();
}
}
装饰模式优点:
1.装饰类和被装饰类可以独立发展,而不会相互耦合
2.装饰模式是继承关系的一个替代方案,不管经过多少层装饰,返回的始终是源对象
3.装饰模式可以动态地扩展一个实现类的功能装饰模式使用场景:
1.需要扩展一个类的功能,或给一个类增加附加功能。
2.需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
3.需要为一批的兄弟类进行改装或加装功能,当然是首选装饰模式。
装饰模式和代理模式区别
在上个例子中类图基本都是一样的,很懵逼既然都是一样为什么还要搞出两个模式,装饰模式和代理模式根本区别是:装饰模式是在要保证接口不变的情况下加强类的功能,它保证的是被修饰的对象功能比原始对象丰富(当然,也可以减弱),但不做准入条件判断和准入参数过滤,如是否可以执行类的功能,过滤输入参数是否合规等,这不是装饰模式关心的。代理模式是把当前的行为或功能委托给其他对象执行,代理类负责接口限定:是否可以调用真实角色,以及是否对发送到真实角色的消息进行变形处理,它不对被主题角色(也就是被代理类)的功能做任何处理,保证原汁原味的调用。
适配器模式
思想:将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
例子:丑小鸭小的时候一直以为是一个鸭子,但实际是一只白天吧,写一个吧天鹅变成鸭子的例子
/**
* 鸭子接口
*/
public interface Duck {
//会叫
public void cry();
//鸭子的外形
public void desAppearance();
//描述鸭子的其他行为
public void desBehavior();
}
/**
* 鸭子实现
*/
public class Duckling implements Duck {
public void cry() {
System.out.println("叫声是嘎——嘎——嘎");
}
public void desAppearance() {
System.out.println("外形是黄白相间,嘴长");
}
//鸭子的其他行为,如游泳
public void desBehavior(){
System.out.println("会游泳");
}
}
/**
* 天鹅抽象接口
*/
public interface Swan {
//天鹅会飞
public void fly();
//天鹅会叫
public void cry();
//天鹅都有漂亮的外表
public void desAppaearance();
}
/**
* 天鹅实现
*/
public class WhiteSwan implements Swan {
//白天鹅的叫声
public void cry() {
System.out.println("叫声是克噜——克噜——克噜");
}
//白天鹅的外形
public void desAppaearance() {
System.out.println("外形是纯白色,惹人喜爱");
}
//天鹅是能够飞行的
public void fly() {
System.out.println("能够飞行");
}
}
/**
* 天鹅适配成鸭子
*/
public class UglyDuckling extends WhiteSwan implements Duck {
//丑小鸭的叫声
public void cry() {
super.cry();
}
//丑小鸭的外形
public void desAppearance() {
super.desAppaearance();
}
//丑小鸭的其他行为
public void desBehavior(){
//丑小鸭不仅会游泳
System.out.println("会游泳");
//还会飞行
super.fly();
}
}
/**
* 客户代码
*/
public class Client {
public static void main(String[] args) {
//鸭妈妈有5个孩子,其中4个都是一个模样
System.out.println("===妈妈有五个孩子,其中四个模样是这样的:===");
Duck duck = new Duckling();
duck.cry(); //小鸭子的叫声
duck.desAppearance(); //小鸭子的外形
duck.desBehavior(); //小鸭子的其他行为
System.out.println("\n===一只独特的小鸭子,模样是这样的:===");
Duck uglyDuckling = new UglyDuckling();
uglyDuckling.cry(); //丑小鸭的叫声
uglyDuckling.desAppearance(); //丑小鸭的外形
uglyDuckling.desBehavior(); //丑小鸭的其他行为
}
}
适配器模式优点
1.适配器模式可以让两个没有任何关系的类在一起运行,只要适配器这个角色能够搞定他们就成
2.提高了类的复用度
3.灵活性非常好
4.增加了类的透明性使用场景
适配器应用的场景只要记住一点就足够了:你有动机修改一个已经投产中的接口时,适配器模式可能是最适合你的模式。比如系统扩展了,需要使用一个已有或新建立的类,但这个类又不符合系统的接口,怎么办?使用适配器模式,这也是我们例子中提到的。
桥接模式
思想:将抽象和实现解耦使得两者可以独立地变化。
例子:公司和产品两个独立的抽象,公司生产产品来赚钱
/** 产品抽象类 */
public abstract class Product {
// 生产产品
public abstract void beProducted();
// 销售产品
public abstract void beSelled();
}
/** 房子产品实现类 */
public class House extends Product {
public void beProducted() {
System.out.println("生产出的房子是这样的...");
}
public void beSelled() {
System.out.println("生产出的房子卖出去了...");
}
}
/** IPod产品实现类 */
public class IPod extends Product {
public void beProducted() {
System.out.println("生产出的iPod是这样的...");
}
public void beSelled() {
System.out.println("生产出的iPod卖出去了...");
}
}
/** 公司抽象类 */
public abstract class Corp {
//定义一个抽象的产品对象 不知道具体是什么产品
private Product product;
//构造函数 由子类定义传具体的产品
public Corp(Product product){
this.product = product;
}
public void makeMoney(){
this.product.beProducted();
this.product.beSelled();
}
}
/** 房地产公司 */
public class HouseCorp extends Corp {
public HouseCorp(House house){
super(house);
}
public void makeMoney(){
super.makeMoney();
System.out.println("房地产公司赚大了...");
}
}
/** 山寨公司生产 假的IPOD 不 iPad */
public class ShanZhaiCorp extends Corp {
public ShanZhaiCorp(Product product){
super(product);
}
public void makeMoney(){
super.makeMoney();
System.out.println("我赚呀...");
}
}
public class Client {
public static void main(String[] args) {
House house = new House();
System.out.println("-------房地产公司是这样运行的-------");
HouseCorp houseCorp =new HouseCorp(house);
houseCorp.makeMoney();
System.out.println("\n");
System.out.println("-------山寨公司是这样运行的-------");
ShanZhaiCorp shanZhaiCorp = new ShanZhaiCorp(new IPod());
shanZhaiCorp.makeMoney();
}
}
桥接模式优点
1.分离抽象接口及其实现部分。提高了比继承更好的解决方案
2.桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。
3.实现细节对客户透明,可以对用户隐藏实现细节桥接模式缺点
桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。桥接模式使用场景
1.不希望或者不适用使用继承的场景
2.接口或抽象类部位定的场景
3.重用性要求较高的场景
外观模式
思想: 求一个子系统的外部与其内部的通信信必须通过一个统一的对象进行。 面模式提供一个高层次的接口,使得子系统更易于使用。
门面模式就不需要类图了比较简单,就是一个模块或者子系统提供一个统一的接口供外部系统调用。
- 门面模式优点
1.减少系统的相互依赖
2.提高灵活性,依赖少了灵活自然高
3.提高安全性,对外只有一个接口
享元模式
思想:使用共享对象可以有效的支持大量的细粒度对象。
享元模式的现实场景:spring-BeanFactory、ThreadPoolExecutor
组合模式
思想:将对象组合成树形结构以表示“ 分-整体”的层次结构 使得用户对单个对象和组合对象的使用具有一致性。
例子:工单员工关系,有高层管理、中层管理和员工,我们就可以用组合关系来实现。
/** 公司职员类 */
public abstract class Corp {
//公司每个人都有名称
private String name = "";
//公司每个人都职位
private String position = "";
//公司每个人都有薪水
private int salary =0;
public Corp(String _name,String _position,int _salary){
this.name = _name;
this.position = _position;
this.salary = _salary;
}
//获得员工信息
public String getInfo(){
String info = "";
info = "姓名:" + this.name;
info = info + "\t职位:"+ this.position;
info = info + "\t薪水:" + this.salary;
return info;
}
}
/** 普通员工 */
public class Leaf extends Corp {
//就写一个构造函数,这个是必需的
public Leaf(String _name,String _position,int _salary){
super(_name,_position,_salary);
}
}
/** 公司领导 */
public class Branch extends Corp {
//领导下边有哪些下级领导和小兵
ArrayList subordinateList = new ArrayList();
//构造函数是必需的
public Branch(String _name,String _position,int _salary){
super(_name,_position,_salary);
}
//增加一个下属,可能是小头目,也可能是个小兵
public void addSubordinate(Corp corp) {
this.subordinateList.add(corp);
}
//我有哪些下属
public ArrayList getSubordinate() {
return this.subordinateList;
}
}
public class Client {
//遍历整棵树,只要给我根节点,我就能遍历出所有的节点
public static String getTreeInfo(Branch root){
ArrayList subordinateList = root.getSubordinate();
String info = "";
for(Corp s :subordinateList){
if(s instanceof Leaf){ //是员工就直接获得信息
info = info + s.getInfo()+"\n";
}else{ //是个小头目
info = info+s.getInfo()+"\n"+ getTreeInfo((Branch)s);
}
}
return info;
}
}
- 组合模式优点:
1.外部使用简单
2.节点增加自由 - 组合模式缺点
和接口兼容不方便,因为每个节点都是一个实现类