设计模式(四):初识设计模式

93 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

上一篇文章介绍了设计模式中的创建型模式,感兴趣的同学请移步设计模式(三):初识设计模式

结构型模式

适配器模式(Adapter Pattern)

适配器模式将某个类的接口转换为客户端期望的另一个接口表示,让原本因接口不匹配不能一起工作的两个类可以协同工作,其别名为包装器(Wrapper)。

适配器模式主要分为三类:类适配器模式、对象适配器模式、接口适配器模式,下面主要演示类适配器模式。

代码演示:

//目标接口
interface Target{
  public void request();
}
//适配者接口
class Adaptee{
    public void specificRequest()
    {
        System.out.println("适配者中的业务代码被调用!");
    }
}
//类适配器类
class ClassAdapter extends Adaptee implements Target
{
    public void request()
    {
        specificRequest();
    }
}

优点提高了类的复用,灵活性好。

缺点过多的使用适配器会让系统显得凌乱,结构不易整体把控。

桥接模式(Bridge Pattern)

桥接模式是指将实现和抽象放在两个不同的类层次中,使两个层次可以独立改变,通过使用封装、聚合及继承等行为让不同的类承担不同的职责,从而可以保持各部分的独立性以及应对他们的功能扩展。

代码演示:

// 品牌接口
public interface Brand {
    void open();
    void close();
    void call();
}

// 小米品牌
public class Xiaomi implements Brand{
    @Override
    public void open() {
        System.out.println("小米手机开机");
    }
    @Override
    public void close() {
        System.out.println("小米手机关机");
    }
    @Override
    public void call() {
        System.out.println("小米手机打电话");
    }
}

// Vivo品牌
public class Vivo implements Brand{
    @Override
    public void open() {
        System.out.println("Vivo手机开机");
    }
    @Override
    public void close() {
        System.out.println("Vivo手机关机");
    }
    @Override
    public void call() {
        System.out.println("Vivo手机打电话");
    }
}

// 抽象手机
public abstract class Phone {
    // 组合品牌
    private Brand brand;
    // 构造
    public Phone(Brand brand) {
        this.brand = brand;
    }
    protected void open() {
        this.brand.open();
    }
    protected void close() {
        this.brand.close();
    }
    protected void call() {
        this.brand.call();
    }
}

// 折叠式手机
public class FoldedPhone extends Phone{
    public FoldedPhone(Brand brand) {
        super(brand);
    }
    public void open() {
        super.open();
        System.out.println("折叠样式手机");
    }
    public void close() {
        super.close();
        System.out.println("折叠样式手机");
    }
    public void call() {
        super.call();
        System.out.println("折叠样式手机");
    }
}

//客户端
public class Client {
    public static void main(String[] args) {
        // 获取折叠式手机 样式+品牌
        FoldedPhone phone1 = new FoldedPhone(new Xiaomi());
        phone1.open();
        phone1.call();
        phone1.close();

        FoldedPhone phone2 = new FoldedPhone(new Vivo());
        phone2.open();
        phone2.call();
        phone2.close();
    }
}

优点抽象和实现的分离。 缺点引入桥接模式会增加对系统的理解和设计难度,要求开发者对抽象层进行编程和设计。

组合模式(Composite Pattern)

组合模式又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示"整体-部分"的层次关系。

代码演示:

public class Employee {
   private String name;
   private String dept;
   private int salary;
   private List<Employee> subordinates;  //下属

   public Employee(String name, String dept, int sal) {
      this.name = name;
      this.dept = dept;
      this.salary = sal;
      subordinates = new ArrayList<Employee>();
   }

   public void add(Employee e) {
      subordinates.add(e);
   }

   public void remove(Employee e) {
      subordinates.remove(e);
   }

   public List<Employee> getSubordinates() {
      return subordinates;
   }
}

优点具有较强的扩展性,方便创建出复杂的层次结构

缺点要求较高的抽象性

装饰器模式(Decorator Pattern)

装饰器模式允许向一个现有的对象添加新的功能,同时不改变其结构。

代码演示:

//观赏接口
public interface Look {
    public String LookAtPic();
}

//观赏实现类
public class InkPic implements Look
{
    @Override
    public String LookAtPic()
    {
        return "欣赏水墨画";
    }
}
public class OilPic implements Look
{
    @Override
    public String LookAtPic() {
        return "欣赏油画";
    }
}

//装饰器抽象类
public abstract class PicDecorator implements Look
{
    private Look look;
 
    public Look getLook() {
        return look;
    }
 
    public void setLook(Look look) {
        this.look = look;
    }
 
    public PicDecorator(Look look) {
        this.look = look;
    }
 
    @Override
    public abstract String LookAtPic();
}

//装饰器实现类
public class WoodPicDecorator extends PicDecorator
{
    public WoodPicDecorator(Look look) {
        super(look);
    }
 
    @Override
    public String LookAtPic() {
        return this.getLook().LookAtPic() + ",木质相框";
    }
}

优点动态添加功能

外观模式(Facade Pattern)

外观模式也叫过程模式,它为子系统中的一组接口提供了一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

代码演示:

//观赏接口
public interface Look {
    public String lookAtPic();
}

//观赏实现类
public class InkPic implements Look
{
    @Override
    public String lookAtPic()
    {
        return "欣赏水墨画";
    }
}
public class OilPic implements Look
{
    @Override
    public String lookAtPic() {
        return "欣赏油画";
    }
}

//外观类
public class LookMaker {
   private Look inkPic;
   private Look oilPic;

   public LookMaker() {
      inkPic = new InkPic();
      oilPic = new OilPic();

   }

   public void showInkPic(){
      inkPic.lookAtPic();
   }
   public void showOilPic(){
      oilPic.lookAtPic();
   }
}

优点对外屏蔽了子系统的细节,降低了客户端对子系统使用的复杂性,让子系统内部的模块更易维护和扩展。

缺点过多的或不合理的使用外观模式,不利于维护

享元模式(Flyweight Pattern)

享元模式运用共享技术有效地支持大量细粒度的对象,也就是避免创建重复的对象,解决重复对象的内存浪费。

模式的角色及职责:

抽象享元角色:产品的抽象类,同时定义出对象的外部状态和内部状态的接口或实现。

具体享元角色:产品的实现类,实现抽象角色定义相关业务。

享元工厂类:用于构建一个池容器(集合),同时提供从池中获取对象方法。

刚刚提到了对象的内部状态和外部状态,内部状态指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变。外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态。

代码演示:

...
private static final HashMap<String, Object> map = new HashMap<>();

public static Object get(String type) {
   Object object = map.get(type);

   if(object == null) {
      object = new Object(type);
      circleMap.put(key, object);
   }
   return object;
}

优点减少了对象的创建,降低系统的内存。

缺点提高了系统的复杂度,划分了外部状态和内部状态。

代理模式(Proxy Pattern)

代理模式为对象提供了一种代理,方便对这个对象进行访问,即通过代理对象访问目标对象。

代理模式有两种,静态代理、动态代理(JDK、Cglib)。

1.静态代理 静态代理在使用时,需要定义接口或父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或是继承相同的父类 代码演示:

//接口
public interface ITeacher{
   void teach();
}

//目标类
public class Teacher implements ITeacher{
   @Override
   public void teach(){
      System.out.println("老师授课中...");
   }
}

//代理类
public class TeacherProxy implements ITeacher{
   private ITeacher target;

   public TeacherProxy(ITeacher target){
      this.target = target;
   }
   @Override
   public void teach(){
      System.out.println("代理操作...");
      target.teach();
      System.out.println("代理操作...");
   }
}

优点在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展

缺点代理对象和目标对象实现相同的接口,一旦接口增加方法,代理对象和目标对象都要维护

2.动态代理

目标对象需要实现接口的使用JDK动态代理,目标对象只是一个单独的对象,并没有实现任何接口的使用Cglib代理。

JDK动态代理实现只需要使用newProxyInstance方法,这个方法需要接收三个参数,完成的写法是:

static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

代码演示:

JDK动态代理

//接口类
public interface ITeacher {
    void teach();
    void sayHello(String name);
}

//目标类
public class Teacher implements ITeacher{

    @Override
    public void teach() {
        System.out.println("老师授课中...");
    }

    @Override
    public void sayHello(String name) {
        System.out.println("hello" + name);
    }
}

//代理类
public class ProxyFactory {
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader()
                , target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("JDK代理开始");
                Object invoke = method.invoke(target, args);
                System.out.println("JDK代理完毕");
                return invoke;
            }
        });
    }
}

如有不对的地方,欢迎大家指正。