• 设计模式——结构型


    一、 外观模式

    提供统一接口,用来访问子系统中的一群接口

    适用:

    • 子系统复杂
    • 构建多层系统结构,利用外观对象作为每层入口

    优点:

    • 简化调用过程,无需深入子系统
    • 减少系统依赖,松散耦合
    • 更好划分访问层次
    • 符合迪米特法则(最少知道)

    缺点:

    • 增加子系统,扩展子系统行为易引入风险
    • 不符合开闭原则
    // 外观类
    public class GiftExchangeService {
    	// 子系统
    	private QualifyService qualifyService = new QualifyService();		
    	private PointsPaymentService pointsPaymentService = new PointsPaymentService();
    	private ShippingService shippingService = new ShippingService();
    
    	// 开放给客户端的接口
    	public void giftExchange(PointsGift pointsGift) {
    		// 内部逻辑
    		if (qualifyService.isAvailable(pointsGift)) {
    			// 资格校验通过
    			if (pointsPaymentService.pay(pointsGift)) {
    				// 如果支付积分成功
    				String shippingOrderNo = shippingService.shipGift(pointsGift);
    				System.out.println("物流系统下单成功,订单号是:" + shippingOrderNo);
    			}
    		}
    	}
    }
    
    // 客户端
    public class Test {
        public static void main(String[] args) {
            PointsGift pointsGift = new PointsGift("T恤");
            GiftExchangeService giftExchangeService = new GiftExchangeService();
            giftExchangeService.giftExchange(pointsGift);
        }
    }
    

    二、 装饰者模式

    在不改变原有对象的基础之上,将功能附加到对象上,扩展原有对象功能

    适用:

    • 扩展一个类的功能或给一个类添加附加职责
    • 动态的给一个对象添加功能,这些功能可以再动态撤销

    优点:

    • 继承的有力补充,比继承灵活,不改变原有对象的情况下给对象扩展功能
    • 通过使用不同装饰类及排列组合,实现不同效果
    • 符合开闭原则

    缺点:

    • 会出现更多的代码,更多的类,增加程序复杂性
    • 动态装饰,多层装饰更加复杂
    // 抽象实体
    public abstract class ABattercake {
    	protected abstract String getDesc();
    
    	protected abstract int cost();
    }
    
    // 确定实体
    public class Battercake extends ABattercake {
        @Override
        protected String getDesc() {
            return "煎饼";
        }
    
        @Override
        protected int cost() {
            return 8;
        }
    }
    
    // 抽象装饰者
    public abstract class AbstractDecorator extends ABattercake {
        private ABattercake aBattercake;
    
        public AbstractDecorator(ABattercake aBattercake) {
            this.aBattercake = aBattercake;
        }
    
        @Override
        protected String getDesc() {
            return this.aBattercake.getDesc();
        }
    
        @Override
        protected int cost() {
            return this.aBattercake.cost();
        }
    }
    
    // 确定装饰者1
    public class EggDecorator extends AbstractDecorator {
    	public EggDecorator(ABattercake aBattercake) {
    		super(aBattercake);
    	}
    
    	@Override
    	protected String getDesc() {
    		return super.getDesc() + " 加一个鸡蛋";
    	}
    
    	@Override
    	protected int cost() {
    		return super.cost() + 1;
    	}
    }
    
    // 确定装饰者2
    public class SausageDecorator extends AbstractDecorator {
    	
    	// ...
    }
    
    // 客户端
    public class Test {
    	public static void main(String[] args) {
    		ABattercake aBattercake;
    		aBattercake = new Battercake();
    		aBattercake = new EggDecorator(aBattercake);
    		aBattercake = new EggDecorator(aBattercake);
    		aBattercake = new SausageDecorator(aBattercake);
    
    		System.out.println(aBattercake.getDesc() + " 销售价格:" + aBattercake.cost());
    	}
    }
    

    三、 适配器模式

    将一个类的接口转换成期望的另一个接口,使得原本接口不兼容的类可以一起工作

    适用:

    • 已经存在的类,方法与需求不匹配,但方法结果相似
    • 随着软件维护,解决不同厂家生产接口不同功能类似的产品问题

    优点:

    • 提高类的透明性和复用,现有的类复用不需要改变
    • 目标类和适配器解耦,提高程序扩展性
    • 符合开闭原则

    缺点:

    • 编写需要全面考虑,可能会增加复杂度
    • 增加系统可读难度
    // Adaptee 经由 Adapter 转换为 Target
    
    // Adaptee
    public class Adaptee {
    	public void adapteeRequest() {
    		System.out.println("被适配者的方法");
    	}
    }
    
    // Target
    public interface Target {
        void request();
    }
    
    // Adpter 类适配器:继承
    public class Adapter extends Adaptee implements Target{
        @Override
        public void request() {
            //...
            super.adapteeRequest();
            //...
        }
    }
    
    // Adapter 对象适配器:组合
    public class Adapter implements Target{
        private Adaptee adaptee = new Adaptee();
    
        @Override
        public void request() {
            //...
            adaptee.adapteeRequest();
            //...
        }
    }
    

    四、 享元模式

    提供类减少对象数量从而改善应用所需的对象结构模式

    运用共享技术有效支持大量细粒度的对象

    适用:

    • 系统底层的开发,解决性能问题
    • 系统有大量相似对象,需要缓冲池的场景

    优点:

    • 减少对象的创建,降低内存中对象数量
    • 减少内存之外其他资源占用

    缺点:

    • 关注内/外状态,关注线程安全问题
    • 程序逻辑复杂

    内部状态:不随外界变化
    外部状态:随外界变化

    public abstract class Flyweight {
    
        // 内部状态
        public String intrinsic;
        // 外部状态
        protected final String extrinsic;
        
        // 要求享元角色必须接受外部状态
        public Flyweight(String extrinsic) {
            this.extrinsic = extrinsic;
        }
        
        // 定义业务操作
        public abstract void operate(int extrinsic);
    
        public String getIntrinsic() {
            return intrinsic;
        }
    
        public void setIntrinsic(String intrinsic) {
            this.intrinsic = intrinsic;
        }
    }
    
    public class ConcreteFlyweight extends Flyweight {
    
        //接受外部状态
        public ConcreteFlyweight(String extrinsic) {
            super(extrinsic);
        }
    
        //根据外部状态进行逻辑处理
        @Override
        public void operate(int extrinsic) {
            System.out.println("具体Flyweight:" + extrinsic);
        }
    
    }
    
    // 享元工厂
    public class FlyweightFactory {
    
        //定义一个池容器
        private static HashMap<String, Flyweight> pool = new HashMap<>();
        
        public static Flyweight getFlyweight(String extrinsic) {
            Flyweight flyweight = null;
            
            if(pool.containsKey(extrinsic)) {   
                flyweight = pool.get(extrinsic);
                System.out.print("已有 " + extrinsic + " 直接从池中取---->");
            } else {
                flyweight = new ConcreteFlyweight(extrinsic);
                pool.put(extrinsic, flyweight);
                System.out.print("创建 " + extrinsic + " 并从池中取出---->");
            }
            
            return flyweight;
        }
    }
    

    五、 组合模式

    将对象组合成树形结构以表示部分——整体层次结构,使得客户端对单个对象和组合对象保持一致的方式处理

    适用:

    • 希望客户端忽略组合对象与单个对象的差异
    • 处理树形结构

    优点:

    • 清除定义分层次的复杂对象,表示对象的全部或部分层次
    • 让客户端忽略层次的差异,方便对整个层次结构进行控制
    • 简化客户端代码
    • 符合开闭原则

    缺点:

    • 限制类型时会较为复杂
    • 使设计变得更加抽象
    // 抽象组件
    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("不支持打印操作");
    	}
    }
    
    // 课程
    public class Course extends CatalogComponent {
    	private String name;
    	private double price;
    
    	public Course(String name, double price) {
    		this.name = name;
    		this.price = price;
    	}
    
    	// 覆盖 getName getPrice print
    }
    
    // 课程目录 包含课程列表
    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 linuxCourse = new Course("Linux课程", 11);
    		CatalogComponent windowsCourse = new Course("Windows课程", 11);
    
    		CatalogComponent baseCourseCatalog = new CourseCatalog("基础课程目录", 2);
    
    		CatalogComponent course1 = new Course("数据结构", 55);
    		CatalogComponent course2 = new Course("高等数学", 66);
    
    		baseCourseCatalog.add(course1);
    		baseCourseCatalog.add(course2);
    
    		CatalogComponent mainCourseCatalog = new CourseCatalog("主目录", 1);
    		mainCourseCatalog.add(linuxCourse);
    		mainCourseCatalog.add(windowsCourse);
    		mainCourseCatalog.add(baseCourseCatalog);
    
    		mainCourseCatalog.print();
    	}
    }
    

    六、 桥接模式

    将抽象部分与具体实现部分分离,使他们可以独立变化

    通过组合建立类之间的联系

    适用:

    • 抽象和具体实现之间增加灵活性
    • 一个类存在多个独立变化的维度,且维度需要独立维护
    • 不希望使用继承,或因为多层继承导致系统类的个数剧增

    优点:

    • 分离抽象部分及其具体实现部分
    • 提高系统可扩展性
    • 符合开闭原则
    • 符合合成/复用原则

    缺点:

    • 增加系统的理解与设计难度
    • 需要正确识别系统中两个独立变化的维度
    // 实现
    public interface Account {
    	Account openAccount();
    
    	void showAccountType();
    }
    
    // 抽象
    public abstract class Bank {
    	protected Account account;	// 桥梁
    
    	public Bank(Account account) {
    		this.account = account;
    	}
    
    	abstract Account openAccount();	// 委托给实现
    }
    
    // 抽象和实现的具体类
    public class DepositAccount implements Account {
        // ...
    }
    
    public class SavingAccount implements Account {
        // ...
    }
    
    public class ABCBank extends Bank {
        public ABCBank(Account account) {
            super(account);
        }
    
        @Override
        Account openAccount() {
            System.out.println("打开中国农业银行账号");
            account.openAccount();
            return account;
        }
    }
    
    public class ICBCBank extends Bank {
        public ICBCBank(Account account) {
            super(account);
        }
    
        @Override
        Account openAccount() {
            System.out.println("打开中国工商银行账号");
            account.openAccount();
            return account;
        }
    }
    

    七、 代理模式

    为其他对象提供一种代理,以控制对这个对象的访问,起到中介的作用

    适用:

    • 保护目标对象
    • 增强目标对象

    优点:

    • 将代理对象与真实被调用的目标对象分离
    • 一定程度上降低系统耦合度,扩展性好
    • 保护目标对象
    • 增强目标对象

    缺点:

    • 类的数目增加

    • 请求处理速度变慢

    • 增加系统的复杂度

    • 静态代理:显式定义代理类,对同名方法进行包装

    public interface BuyHouse {
        void buyHouse();
    }
    
    public class BuyHouseImpl implements BuyHouse {
    
        @Override
        public void buyHouse() {
            System.out.println("我要买房");
        }
    }
    
    
    public class BuyHouseProxy implements BuyHouse {
    
        private BuyHouse buyHouse;
    
        public BuyHouseProxy(final BuyHouse buyHouse) {
            this.buyHouse = buyHouse;
        }
    
        @Override
        public void buyHouse() {
            beforeBuyHouse();
            
            buyHouse.buyHouse();
            
            afterBuyHouse();
        }
    
        private void beforeBuyHouse() {
    		System.out.println("买房前准备");
        }
    
        private void afterBuyHouse() {
        	System.out.println("买房后装修");
        }
    }
    
    • 动态代理:编写动态处理器,并利用反射,仅支持 interface 代理
    public class DynamicProxyHandler implements InvocationHandler {
    
    	private Object object;
    
    	public DynamicProxyHandler(final Object object) {
    		this.object = object;
    	}
    
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		System.out.println("买房前准备");
    		Object result = method.invoke(object, args);
    		System.out.println("买房后装修");
    		return result;
    	}
    }
    
    public class Test {
    
    	public static void main(String[] args) {
    		BuyHouse proxyBuyHouse = (BuyHouse) Proxy.newProxyInstance(
    			BuyHouse.class.getClassLoader(), 				// ClassLoader loader:指定当前目标对象使用的类加载器
    			BuyHouseImpl.class.getInterfaces(), 			// Class<?>[] interfaces:指定目标对象实现的接口的类型
    			new DynamicProxyHandler(new BuyHouseImpl()));	// InvocationHandler:指定动态处理器
    		proxyBuyHouse.buyHouse();
    	}
    }
    
    • CGLib代理:通过继承进行代理
    public class CglibProxy implements MethodInterceptor {
    	private Object target;
    
    	public Object getInstance(final Object target) {
    		this.target = target;
    		Enhancer enhancer = new Enhancer();
    		enhancer.setSuperclass(this.target.getClass());
    		enhancer.setCallback(this);
    		return enhancer.create();
    	}
    
    	public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    		System.out.println("买房前准备");
    		Object result = methodProxy.invoke(object, args);
    		System.out.println("买房后装修");
    		return result;
    	}
    }
    
    public class Test {
        
        public static void main(String[] args){
            CglibProxy cglibProxy = new CglibProxy();
            BuyHouseImpl buyHouseCglibProxy = (BuyHouseImpl) cglibProxy.getInstance(new BuyHouseImpl());
            buyHouseCglibProxy.buyHosue();
        }
    }
    
  • 相关阅读:
    确保EF上下文线程内唯一
    linq的join
    编码:隐匿在计算机软硬件背后的语言
    EF删除数据
    插入数据返回主键值用 output inserted.UId
    Fancybox丰富的弹出层效果
    回车登录
    “:Choose a destination with a supported architecture in order to run on this device.”
    How to Enable Multi-Touch
    How does CCFileUTils::fullPathForFilename work
  • 原文地址:https://www.cnblogs.com/JL916/p/12643004.html
Copyright © 2020-2023  润新知