- 从程序的结构上解决模块之间的耦合问题
6.4.1. 代理模式
- 代理模式也被称为委托模式。
- 定义:为其他对象提供一种代理以控制对这个对象的访问。
1. 抽象主题类
- 抽象主题类具有真实主题类和代理的共同接口方法,共同的方法就是购买
package com.example.mode2;
public interface IShop {
void buy();
}
2. 真实主题类
- 购买者就是我本人,实现了IShop提供的buy()方法
package com.example.mode2;
public class Ning implements IShop{
@Override
public void buy() {
System.out.println("购买");
}
}
3. 代理类
- 代理类同样需要实现IShop接口,并且要持有被代理者,在buy()方法中调用被代理者的buy()方法
package com.example.mode2;
public class purchaseProxy implements IShop{
private IShop mShop;
public purchaseProxy(IShop iShop){
mShop = iShop;
}
@Override
public void buy() {
mShop.buy();
}
}
4. 客户端实现
- 客户端类的代码就是代理类包含了真实主题类(被代理者),最终调用真实主题类实现的方法。
package com.example.mode2;
public class Client {
public static void main(String[] args) {
IShop ning = new Ning();
IShop purchasing = new purchaseProxy(ning);
purchasing.buy();
}
}
动态代理的实现
- 从编码角度,代理模式分为静态代理和动态代理。
- 上面的例子是静态代理,在代码运行前就已经存在了代理类的class编译文件
- 而动态代理则是在代码运行时通过反射来动态地生成代理类的对象。
- Java提供了动态的代理接口InvocationHandler,实现该接口需要重写invoke方法。
package com.example.mode2;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicPurchasing implements InvocationHandler {
private Object object;
public DynamicPurchasing(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(object , args);
//获取方法名
if(method.getName().equals("buy")){
System.out.println("Ning在买东西");
}
return result;
}
}
- 接下来修改客户端类的代码
package com.example.mode2;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
//创建Ning
IShop ning = new Ning();
//创建动态代理
DynamicPurchasing mDynamicPurchasing = new DynamicPurchasing(ning);
//创建Ning的ClassLoader
ClassLoader loader = ning.getClass().getClassLoader();
//动态创建代理类
IShop purchasing = (IShop) Proxy.newProxyInstance(loader , new Class[]{IShop.class} , mDynamicPurchasing);
purchasing.buy();
}
}
6.4.2 装饰模式
- 其在不改变类文件和使用继承的情况下,动态地拓展了一个对象的功能,它是继承的替代方案之一。
- 定义:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
- 杨过本身会全真剑法。洪七公和欧阳锋教了他打狗棒法和蛤蟆功。
这样杨过就学会了三个功法。洪七公和欧阳锋就起到了装饰杨过的作用。
1. 抽象组件
- 首先定义一个武侠的抽象类,里面有使用武功的抽象方法。
public abstract class Swordsman{
public abstract void attackMagic();
}
2. 组件的具体实现类
- 被装饰的具体对象,这里就是被教授武学的具体武侠,也就是杨过。杨过作为武侠当然也会武学
public class YangGuo extends Swordsman{
@Override
public void attackMagic(){
//杨过初始的武学是全真剑法
System.out.println("使用剑法");
}
}
3. 抽象装饰类
- 抽象装饰者保持了一个对抽象组件的引用,方便调用被装饰对象中的方法。
- 在这个例子中,就是武学前辈要持有武侠的引用,方便教授他武学并使它融会贯通。
public abstract class Master extends Swordsman{
private Swordsman mSwordsman;
public Master(Swordman mSwordsman){
this.mSwordsman = mSwordsman;
}
@Override
public void attackMagic(){
mSwordsman.attackMagic();
}
}
4. 装饰者具体实现类
- 用两个装饰者具体实现类,他们负责向杨过传授新的武功
public class HongQiGong extends Master{
public HongQiGong(Swordsman mSwordsman){
super(mSwordsman);
}
public void teachAttackMagic(){
System.out.println("洪七公教授打狗棒法");
System.out.println("杨过使用打狗棒法");
}
@Override
public void attackMagic(){
super.attackMaigc();
teachAttackMagic();
}
public class OuYangFeng extends Master{
public OuYangFeng(Swordsman mSwordsman){
super(mSwordsman);
}
public void teachAttackMagic(){
System.out.println("欧阳锋使用蛤蟆功");
System.out.println("杨过使用蛤蟆功");
}
@Override
public void attackMagic(){
super.attackMagic();
teachAttackMagic();
}
}
5. 客户端调用代码
public class Client{
public static void main(String[] args){
YangGuo mYangGuo = new YangGuo();
HongQiGong mHongQiGong = new HongQiGong(mYangGuo);
mHongQiGong.attackMagic();
OuYangFeng mOuYangFeng = new OuYangFeng(mYangGuo);
mOuYangFeng.attackMagic();
}
}
- 装饰者具体实现类是最后的对象
6.4.3 外观模式
- 外观模式也叫做门面模式。无论是做SDK还是封装API大多都会用到外观模式。
- 它通过一个外观类使得整个系统的结构只有一个统一的高层接口,这样能降低用户的使用成本。
- 定义:要求一个子系统的外部与内部的通信必须通过一个统一的对象进行。此模式必须提供一个高层的接口,使得子系统更易于使用。
- 本节还举武侠的例子。首先,我们把武侠张无忌当作一个系统,张无忌作为一个武侠,它本身分为3个系统,分别是招式、内功和经脉。
1. 子系统类
- 张无忌有很多武学和内功,怎么将它们搭配并对外界隐藏呢?
//1.子系统招式
public class ZhaoShi{
public void TaiJiQuan(){
System.out.print("使用招式太极拳");
}
public void QiShangQuan(){
System.out.print("使用招式七伤拳");
}
public void ShengHuo(){
System.out.print("使用招式圣火令");
}
}
//2.子系统内功
public class NeiGong{
public void JiuYang(){
System.out.print("使用九阳神功");
}
public void QianKun(){
System.out.print("使用乾坤大挪移");
}
}
//3.子系统经脉
public class JingMai{
public void jingmai(){
System.out.print("开启经脉");
}
}
2. 外观类
- 这里的外观类就是张无忌,他负责将自己的招式、内功和经脉通过不同的情况合理地运用
- 初始化外观类的同时将各个子系统创建好。
- 很明显,这里将内部子系统进行了搭配。
- 使用七伤拳,就需要开启经脉,使用内功九阳神功,然后再使用七伤拳。
public class ZhangWuJi{
private JingMai jingMai;
private ZhaoShi zhaoShi;
private NeiGong neiGong;
public ZhangWuJi(){
jingMai = new JingMai();
zhaoshi = new ZhaoShi();
neiGong = new NeiGong();
}
//使用乾坤大挪移
public void QianKun(){
jingMai.jingmai();//开启经脉
neiGong.QianKun();//使用内功乾坤大挪移
}
//使用七伤拳
public void QiShang(){
jingMai.jingmai();//开启经脉
neiGong.JiuYang();//使用内功九阳神功
zhaoShi.QiShangQuan();//使用招式七伤拳
}
}
3. 客户端调用
- 外观模式本身就是将子系统的逻辑和交互隐藏起来,为用户提供一个高层次的接口,使得系统更加易用,同时隐藏了具体的实现。
- 这样即使具体的子系统发生了变化,用户也不会感知到。
public class Client{
public static void main(String[] args){
ZhangWuJi zhangWuJi = new ZhangWuJi();
zhangWuJi.QianKun();
zhangWuJi.QiShang();
}
}
6.4.4 享元模式
- 享元模式是池技术的重要实现方式,它可以减少应用程序的创建,降低程序内存的占用,提高程序的性能。
- 定义:使用共享对象有效地支持大量细粒度的对象
- 细粒度对象:不可避免地使用对象数量多且性质相近的。这些对象分为两部分:内部状态和外部状态。内部状态是对象可共享出来的信息,存储在享元对象内部并且不会随环境的改变而改变。外部状态是对象依赖的一个标记,它是随环境改变而改变的并且不可共享的状态。
- 某网上商城卖商品,如果每个用户
- 那么订单量会生成很多商品对象,更何况商品的种类繁多,这样就会产生OOM。因此我们用享元模式来对商品创建进行优化。
1. 抽象享元角色
- 抽象享元角色是一个商品接口,它定义了showGoodsPrice方法来展示商品的价格
package com.example.mode2;
public interface IGoods {
public void showGoodsPrice(String name);
}
2. 具体享元角色
- 定义类Goods,它实现了IGoods接口,并实现了showGoodsPrice方法
- 其中name为内部状态,version为外部状态。 showGoodsPrice会根据version的不同打印出不同的价格。
package com.example.mode2;
public class Goods implements IGoods{
private String name;//名称
private String version;//版本
Goods(String name){
this.name = name;
}
@Override
public void showGoodsPrice(String name) {
if(version.equals("32G")){
System.out.println("价格为5199元");
}else if(version.equals("128G")){
System.out.println("价格为5999元");
}
}
}
3. 享元工厂
- 用来创建Goods对象。
- 通过Map容器来存储Goods对象,将内部状态name作为Map的key,以便标识Goods对象。
- 如果包含,则使用缓存对象,不包含就新创建Goods对象。
package com.example.mode2;
import java.util.HashMap;
import java.util.Map;
public class GoodsFactroy {
//静态变量
private static Map<String , Goods> pool = new HashMap<String , Goods>();
public static Goods getGoods(String name){
if(pool.containsKey(name)){
System.out.println("使用缓存,key为:" + name);
return pool.get(name);
}else{
Goods goods = new Goods(name);
pool.put(name , goods);
System.out.println("创建商品,key为:" + name);
return goods;
}
}
}
4. 在客户端调用
package com.example.mode2;
public class Client2 {
public static void main(String[] args) {
Goods goods1 = GoodsFactroy.getGoods("iphone7");
goods1.showGoodsPrice("32G");
Goods goods2 = GoodsFactroy.getGoods("iphone7");
goods2.showGoodsPrice("32G");
Goods goods3 = GoodsFactroy.getGoods("iphone7");
goods3.showGoodsPrice("128G");
}
}
- 享元模式的使用场景:系统中存在大量的相似对象。需要缓冲池的场景。