java----设计模式--结构型模式(GOF23)

核心作用:是从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题。

 适配器设计模式:

  将一个类的接口转换成客户希望的另一个的接口

  适配器使原本由于接口不兼容而不能工作的那些类可以一起工作

  适配器比较经典的案例

java.io.InputStreamReader(InputStream)
java.io.OutputStreamWriter(OutputStream)

                              

感觉是配置有一点类似静态代理,但是适配器可能需要继承或者实现某一个类或者接口

  采用组合的方式(推荐使用)

public class Demo {
    public static void main(String[] args){
        work(new PowerAachieve());
         
        //work(new PowerBachieve());  //接口A接受不了接口B
        Adapter adapter = new Adapter(new PowerBachieve()); //利用适配器接入接口A。接口B接入到适配器上即可
        work(adapter);
    }
    public static void work(PowerA power){  //接口A(可以接受是适配器,不能接受接口B)
        power.insert();
    }
}
 
interface PowerA{
    void insert();
}
 
class PowerAachieve implements PowerA{
    public void insert(){
        System.out.println("接口A开始工作");
    }
}
 
 
//适配器
class Adapter implements PowerA{  //适配器必须继承接口B,将接口B接入到适配器上
    private PowerB power;
    public Adapter(PowerB power){
        this.power = power;
    }
    public void insert(){
        this.power.connect();
    }
}
 
interface PowerB{
    void connect();
}
class PowerBachieve implements PowerB{
    public void connect(){
        System.out.println("接口B开始工作");
    }
}

  采用继承的方式(适配器继承需要调用的类,不推荐使用,作为了解)

public class Demo {
    public static void main(String[] args){
        Adapter adapter = new Adapter(); //利用适配器接入接口A。接口B接入到适配器上即可
        work(adapter);
    }
    public static void work(PowerA power){  //接口A(可以接受是适配器,不能接受接口B)
        power.insert();
    }
}
interface PowerA{
    void insert();
}
class PowerAachieve implements PowerA{
    public void insert(){
        System.out.println("接口A开始工作");
    }
}
class Adapter extends PowerBachieve implements PowerA{  //适配器必须继承接口B,将接口B接入到适配器上
    public void insert(){
        this.connect();
    }
}
interface PowerB{
    void connect();
}
class PowerBachieve implements PowerB{
    public void connect(){
        System.out.println("接口B开始工作");
    }
}

  适配器用途二

public class Demo {
    public static void main(String[] args){
        work(new Dog());   
    }
    public static void work(Animal animal){  //接口Animal(可以接受是适配器)
        animal.run();
    }
}
 
interface Animal{
    void run();
    void sing();
    void swim();
    void fly();
}
 
//class Dog implements Animal{
//  public void run(){};
//  public void sing(){};
//  public void swim(){
//      System.out.println("如果只要实现swim方法,但是需要将所有继承的方法都是实现(可以不写其代码)");
//  };
//  public void fly(){};
//}
 
//利用适配器实现所有的接口,其他的类直接继承适配器.这样的好处是不用每一个类都要实现所有的方法.
abstract class Adapter implements Animal{
    public  void run(){};
    public  void sing(){};
    public  void swim(){};
    public  void fly(){};
}
 
class Dog extends Adapter{
    public void run(){
        System.out.println("我可以跑");
    }
}

 

 静态代理模式:

  注意代理的核心思想就是做AOP

  在运行前就已经将代理写好了

  为其他对象提供一种代理以控制对这个对象的访问

  被代理的代码有完整的业务实现,可以单独运行。而通过代理可以添加一些其他的需求。然后通过代理来控制被代理的业务实现。

  静态代理类对象是由自己实现的:缺陷,如果被代理的对象需要增加方法,那么代理类也需要额外添加对应的方法,而且会造成代码冗余。

public class Demo {
    public static void main(String[] args){
        Proxy proxy = new Proxy(new Phone());
        proxy.work();
    }
}

interface Action{
    void work();
}
class Phone implements Action{  //被代理
    public void work(){
        System.out.println("手机开始工作");
    }
}
//代理类必须实现被代理类的所有的方法,所以需要实现被代理类的所有的方法;
class Proxy implements Action{
    private Action action;
    public Proxy(){}
    public Proxy(Action action){
        this.action = action;
    }
    public void work(){
        long starttime = System.currentTimeMillis();
        this.action.work();
        long endtime = System.currentTimeMillis();
        System.out.println("一共耗时:"+(endtime - starttime)+"毫秒");
    }
}

 

 动态代理模式:

  这样创建出来的代理类是什么样的?

    大概是程序会自动生成一个代理类,这给代理类实现了所有的接口(我们传入的),代理类对象就会实现接口中的所有的方法,方法里面的逻辑都是去调用invoke()方法;

  动态代理,就是根据对象在内存中加载的Class类创建运行时类对象,从而调用代理类方法和属性。

public class Demo {
    public static void main(String[] args) {
        CreateProxy createproxy = new CreateProxy();
        Customer customer = new Customer();
        //注意只能强转成接口类型 Subject
        Subject subject = (Subject) createproxy.create(customer);
        subject.shopping(); //会调用invoke方法
    }
}
interface Subject {
    void shopping();
}
class Customer implements Subject {
    @Override
    public void shopping() {
        System.out.println("付款,购买物品");
    }
}
//用于动态生成一个代理对象
class CreateProxy implements InvocationHandler {
    //传入target的目的是因为invoke(target, args)方法需要传入一个target对象,这个对象就是被代理的对象
    private Object target;

    public Object create(Object target) {
        this.target = target;
        //newProxyInstance参数类加载器,接口数组,this,接口是动态加载的(可以调用多个接口)
        //ClassLoader loader:类加载器
        //Class<?>[] interfaces:接口数组,可以写成这样:new Class[]{Subject.class}
        //InvocationHandler h: 实现InvocationHandler接口的类
        Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        return proxy;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName()); //可以用它来判断特定的方法执行不同的功能;
        System.out.println("正在挑选商品.....");
        System.out.println("已挑选好商品.....,等待客户确认订单");
        method.invoke(target, args);
        System.out.println("订单完成");
        return null;
    }
}

静态代理和动态代理的对比

参考:https://www.cnblogs.com/baizhanshi/p/6611164.html 写的很详细

静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。

  优点:代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦合)

  缺点:1、代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

     2、代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。

动态:在程序运行时运用反射机制动态创建而成。

  优点:如果静态代理类只能为一个接口服务,这样程序开发中必然会产生许多的代理类,而动态代理,只需要一个代理类,在代理类中添加不同的代理方法(返回不同的代理对象)即可。

                 不需要实现委代理类的每一个实现方法,减少代码的冗余

 桥接模式:

采用组合方式解决多继承的复杂模式

桥接模式核心要点:
  -处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立的继承结构,使各个维度可以独立的扩展在抽象层建立关联。

使用场景

  --JDBC驱动程序
  --AWT中的Peer架构
  --银行日志管理:
    ·格式分类:操作日志、交易日志、异常日志
    ·距离分类:本地记录日志、异地记录日志
  --人力资源系统中的奖金计算模块:
    奖金分类:个人奖金、团体奖金、激励奖金。·

    部门分类:人事部门、销售部门、研发部门。
  --OA系统中的消息处理:
    业务类型:普通消息、加急消息、特急消息
    发送消息方式:系统内消息、手机短信、邮件

需求:有一个品牌电脑A和B,他们下面都要有2个销售的类型(笔记本和台式机),所以笔记本类和台式机类搜需要继承各自的品牌类

如果现在又要添加一个新的品牌,就又要添加2个销售的类型(这会造成代码冗余,笔记本类的代码都是一样的);

我们可以将品牌和笔记本/台式机类采用组合的方式,桥接起来

public interface Brand {
    void sale();
}
class Lenovo implements Brand{
    @Override
    public void sale() {
        System.out.println("销售联想电脑");
    }
}
class Dell implements Brand{
    @Override
    public void sale() {
        System.out.println("销售Dell电脑");
    }
}
public class Computer {
    private Brand brand;
    public Computer(Brand brand) {
        this.brand = brand;
    }
    public void sale(){
        this.brand.sale();
    }
}

class Desktop extends Computer{
    public Desktop(Brand brand) {
        super(brand);
    }
    @Override
    public void sale() {
        super.sale();
        System.out.println("销售台式机");
    }
}
class laptop extends Computer{
    public laptop(Brand brand) {
        super(brand);
    }
    @Override
    public void sale() {
        super.sale();
        System.out.println("销售笔记本");
    }
}

  demo

public class Demo {
    public static void main(String[] args) {
        Desktop desktop = new Desktop(new Lenovo());
        desktop.sale();
    }
}

 

 组合模式: 

组合和组合设计模式不一样

组合在A类里边添加一个B类这个属性(可以通过构造方法传入B类对象),此时A类就可以调用B类的所有的方法了

使用场景

  把部分和整体的关系用树形结构(类似二叉树)来表示,从而使客户端可以使用统一的方式处理部分对象和整体对象。

组合模式核心:
  -抽象构件(Component)角色:定义了叶子和容器构件的共同点
  -叶子(Leaf)构件角色:无子节点
  -容器(Composite)构件角色:有容器特征,可以包含子节点

开发中的应用场景:
  -操作系统的资源管理器-GUI中的容器层次图
  -XML文件解析
  -OA系统中,组织结构的处理
  -Junit单元测试框架
    底层设计就是典型的组合模式,TestCase(叶子)、TestUnite(容器)、Test接口(抽象)

  抽象组件

public interface Component {
    void operation();
}
//容器节点
interface Composite extends Component{
    void add(Component c);
    void remove(Component c);
    Component getChild (int index);
}
//叶子节点
interface leaf extends Composite{

}

  使用组合模式,模拟杀毒软件架构设计

public interface AbstractFile {
    void killVirus();
}
//叶子节点
class ImageFile implements AbstractFile{
    @Override
    public void killVirus() {
        System.out.println("ImageFile正在被查杀");
    }
}
//叶子节点
class TextFile implements AbstractFile{
    @Override
    public void killVirus() {
        System.out.println("TextFile正在被查杀");
    }
}
//容器节点
class Folder implements AbstractFile{
    private List<AbstractFile> list = new ArrayList<AbstractFile>(); //通过容器节点将各个节点关联起来
    @Override
    public void killVirus() {
        System.out.println("Folder进行查杀");
        list.forEach((AbstractFile abstractFile)->{abstractFile.killVirus();});
    }
    void add(AbstractFile c){
        list.add(c);
    };
    void remove(AbstractFile c){
        list.remove(c);
    };
    AbstractFile getChild (int index){
        AbstractFile abstractFile = list.get(index);
        return abstractFile;
    };
}

  demo

public class Demo {
    public static void main(String[] args) {
        Folder folder = new Folder();
        folder.add(new TextFile());
        folder.add(new ImageFile());
        folder.add(new TextFile());
        //操作容器
        folder.killVirus();
        //操作叶子
        new TextFile().killVirus();
    }
}

 

 装饰者设计模式: 

动态为对象添加新的功能(可以修改对象的属性,可以进行方法的扩展)

开发中使用的场景:
  -IO中输入流和输出流的设计
  -Swing包中图形界面构件功能
  -Servlet API 中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper,HttpServletRequestWrapper类,增强了request对象的功能。
  -Struts2中,request,response,session对象的处理

public class Test {
    public static void main(String[] args){
        Milk milk = new Milk();
        Coffer coffer = new Coffer(milk);
        Coke coke = new Coke(coffer);
        System.out.println(coke.cost()+","+coke.desc());
    }
}
interface Drink{
    float cost();
    String desc();
}
 
class Milk implements Drink{
    @Override
    public float cost() {
        return 10f;
    }
    @Override
    public String desc() {
        return "牛奶";
    }
}
//如果没有这个所有的修饰类都需要添加Drink属性,代码冗余
abstract class Decorator implements Drink{
    private Drink drink;
    public Decorator(Drink drink){
        this.drink = drink;
    }
    @Override
    public float cost() {
        return this.drink.cost();
    }
    @Override
    public String desc() {
        return this.drink.desc();
    }
}
 
class Coffer extends Decorator{
    public Coffer(Drink drink) {
        super(drink);
    }
    @Override
    public float cost() {
        return super.cost()+1.0f;
    }
    @Override
    public String desc() {
        return super.desc()+"加咖啡";
    }
}
class Coke extends Decorator{
    public Coke(Drink drink) {
        super(drink);
    }
    @Override
    public float cost() {
        return super.cost()+1.0f;
    }
    @Override
    public String desc() {
        return super.desc()+"加可乐";
    }
}

 

 外观模式:

迪米特法则(最少知识原则)
一个软件实体应当尽可能少的与其他实体发生相互作用。

外观模式核心
  为子系统提供统一的入口。封装子系统的复杂性,便于客户端调用。

                   

 

 

class A{
    void test(){
        System.out.println("去工商局");
    }
}
class B{
    void test(){
        System.out.println("去税务局");
    }
}
public class Door {
    //注册公司
    void register(){
        new A().test();
        new B().test();
    }
}

  demo

public class Demo {
    public static void main(String[] args) {
        //new A().test();
        //new B().test();
        //只需要和这一个类打交道
        new Door().register();
    }
}

 

 享元模式:

场景:
  内存属于稀缺资源,不要随便浪费。如果有很多个完全相同或相似的对象,我们可以通过享元模式,节省内存,但是增加了运行时间;
核心:
  享元模式以共享的方式高效地支持大量细粒度对象的重用。

  享元对象能做到共享的关键是区分了内部状态和外部状态。
    内部状态:可以共享,不会随环境变化而改变

    外部状态:不可以共享,会随环境变化而改变

比如一个围棋中,所有的棋子大小形状颜色都是可以共享的,但是棋子的位置不能共享

享元模式实现

  普通类:包含了所有的共享变量,有一个方法可以传入非共享变量(该变量不能被存储,所以方法结束变量清空,所以需要在方法中将非共享变量作用使用出来);

  享元工厂:使用单例缓冲值,同一个key的对象只能有一个(如果全部对象都只有一个key,完全可以用单例来代替享元工厂)

开发过程中应用场景

  享元模式由于其共享的特性,可以在任何“池"中操作,比如:线程池、数据库连接池。
  String类的设计也是享元模式

 

//享元类
public interface ChessFlyWeight {
    void setColor(String c);
    String getColor();
    void display(Coordinate coordinate);
}
class Coordinate{
    public int x;
    public int y;
    public Coordinate(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

class ConcreteChess implements ChessFlyWeight{
    public String color;
    public ConcreteChess(String color) {
        this.color = color;
    }

    @Override
    public void setColor(String c) {
        color =c;
    }

    @Override
    public String getColor() {
        return color;
    }

    @Override
    public void display(Coordinate coordinate) {
        System.out.println("棋子颜色:"+color);
        System.out.println("棋子位置:x="+coordinate.x+",y="+coordinate.y);
    }
}

//享元工厂类(核心使用了单例缓冲池)
class ChessFlyWeightFactory{
    //享元池
    private static Map<String,ChessFlyWeight>  map= new HashMap<>();

    public static ChessFlyWeight getChess(String color){
        ChessFlyWeight chessFlyWeight = map.get(color);
        if (chessFlyWeight==null){
            chessFlyWeight = new ConcreteChess(color);
            map.put(color,chessFlyWeight);
        }
        return chessFlyWeight;
    }
}

class Demo{
    public static void main(String[] args) {
        ChessFlyWeight chess = ChessFlyWeightFactory.getChess("黑");
        ChessFlyWeight chess1 = ChessFlyWeightFactory.getChess("黑");
        System.out.println(chess==chess1);

        //非共享的变量从外部传递
        chess.display(new Coordinate(1,1));
        chess1.display(new Coordinate(2,2));
    }
}

 

posted @ 2019-04-06 11:12  小名的同学  阅读(118)  评论(0编辑  收藏  举报