设计模式相关

本文详细介绍了Java中的设计模式,包括单例模式的实现方式和优缺点,工厂方法模式的分类和应用场景,简单工厂模式与抽象工厂模式的对比,以及代理模式的静态和动态代理实现。此外,还讲解了外观模式的概念、角色、实现和使用场景。

目录

设计模式概述

设计模式分类

软件设计七大原则(OOP原则)

单例模式

什么是单例模式?

 怎么保证内存中只有一个对象

饿汉式写法-类一加载就创建对象

懒汉式写法-用的时候才创建对象

选择哪一种?

补充:产生线程安全问题的三个要素

优缺点

使用场景

JDK中单例设计模式的体现-Runtime

工厂方法模式

简单工厂模式

使用场景:

简单工厂和工厂方法模式比较

抽象工厂模式

使用场景:

三种工厂参考:

原型模式

使用原型模式的优点:

使用场景:

补充:浅拷贝和深拷贝

结构型模式

装饰模式

使用场景

代理模式

静态代理模式:

动态代理模式:

外观模式

是什么?

角色

实现

外观的优缺点:

使用场景:

组合模式

定义

实现

使用场景

行为型模式

观察者模式

使用场景

责任链模式

是什么

责任链模式的主要角色

代码实现

优缺点


设计模式概述

设计模式分类

设计模式根据工作的目的,分为创建型模式、结构型模式和行为型模式三类。

创建型模式:单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、创建者模式(建造者)、原型模式。

结构型模式:适配器模式、代理模式、装饰器模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

软件设计七大原则(OOP原则)

开闭原则:对扩展开放,对修改关闭。

里氏替换原则:不要破坏继承体系,子类重写方法功能发生改变,不应该影响父类方法的含义。

依赖倒置原则:要面向接口编程,不要面向实现编程。

单一职责原则:控制类的粒度大小、将对象解耦、提高其内聚性。

接口隔离原则:要为各个类建立它们需要的专用接口。

迪米特法则:一个类应该保持对其它对象最少的了解,降低耦合度。

合成复用原则:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

实际上,七大原则的目的只有一个:降低对象之间的耦合,增加程序的可复用性、可扩展性和可维护性。

单例模式

什么是单例模式?

 怎么保证内存中只有一个对象

饿汉式写法-类一加载就创建对象

懒汉式写法-用的时候才创建对象

选择哪一种?

在开发中,我们会使用饿汉式,因为这种方式不会出现线程安全问题

补充:产生线程安全问题的三个要素

  1. 是否有多线程环境
  2. 是否有共享数据
  3. 是否有多条语句操作共享数据

优缺点

使用场景

  • 要求生成唯一序列号的环境;
  • 在整个项目中需要一个共享访问点或共享数据,例如一个Web页面上的计数 器,可以不用把每次刷新都记录到数据库中,使用单例模式保持计数器的值,并 确保是线程安全的;
  • 创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源;
  • 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式 (当然,也可以直接声明为static的方式)。

JDK中单例设计模式的体现-Runtime

工厂方法模式

实例化对象不是用new,用工厂方法替代。将选择实现类,创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。

用来生产同一等级架构中的固定产品,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。(支持增加任意产品)

//创建手机接口
public interface Phone {
    void name();
}
//创建华为实现类
public class HuaWei implements Phone{
    @Override
    public void name() {
        System.out.println("华为手机");
    }
}
//创建手机工厂接口
public interface PhoneFactory {
    Phone getPhone();
}
//创建华为工厂
public class HuaWeiFactory implements PhoneFactory{
    @Override
    public Phone getPhone() {
        return new HuaWei();
    }
}
//测试类
public class Consumer {
    public static void main(String[] args) {
        Phone phone = new HuaWeiFactory().getPhone();
        phone.name();
    }
}

简单工厂模式

用来生产同一等级架构中的任意产品(对于增加新的产品,需要修改已有代码)
在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例。

//创建手机接口
public interface Phone {
    void name();
}
//创建华为实现类
public class HuaWei implements Phone{
    @Override
    public void name() {
        System.out.println("华为手机");
    }
}
//创建小米实现类
public class XiaoMi implements Phone{
    @Override
    public void name() {
        System.out.println("小米手机");
    }
}
//创建工厂
public class PhoneFactory {
    public static Phone getPhone(String phone){
        if(phone.equals("华为")){
            return new HuaWei();
        }else if(phone.equals("小米")){
            return  new XiaoMi();
        }else {
            return null;
        }
    }
}
//测试类
public class Consumer {
    public static void main(String[] args) {
        Phone p1= PhoneFactory.getPhone("华为");
        Phone p2= PhoneFactory.getPhone("小米");
        p1.name();
        p2.name();
    }
}

使用场景:

jdbc连接数据库,硬件访问,降低对象的产生和销毁

简单工厂和工厂方法模式比较

抽象工厂模式

提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类(围绕一个超级工厂创建其他工厂,该超级工厂称为工厂的工厂)

//电脑接口
public interface Computer {
    void play();
    void watch();
}
//创建华为电脑对象
public class HuaWeiComputer implements Computer{
    @Override
    public void play() {
        System.out.println("HuaWei's play!");
    }
 
    @Override
    public void watch() {
        System.out.println("HuaWei's watch!");
    }
}
//手机接口
public interface Phone {
    void send();
    void call();
}
//创建华为手机对象
public class HuaWeiPhone implements Phone{
    @Override
    public void send() {
        System.out.println("HuaWei's send");
    }
 
    @Override
    public void call() {
        System.out.println("HuaWei's call");
    }
}
//抽象工厂
public interface IProductFactory {
    //生产手机
    Phone phone();
    //生产电脑
    Computer computer();
}
//创建华为工厂
public class HuaWeiFactory implements IProductFactory{
    @Override
    public Phone phone() {
        return new HuaWeiPhone();
    }
 
    @Override
    public Computer computer() {
        return new HuaWeiComputer();
    }
}
//测试类
public class Consumer {
    public static void main(String[] args) {
        HuaWeiFactory huaWeiFactory = new HuaWeiFactory();
        Phone phone = huaWeiFactory.phone();
        phone.call();
        phone.send();
        Computer computer = huaWeiFactory.computer();
        computer.play();
        computer.watch();
    }
}

使用场景:

一个对象族(或是一组没有任何关系的对象)都有相同的约束。 涉及不同操作系统的时候,都可以考虑使用抽象工厂模式

三种工厂参考:

https://blog.csdn.net/weixin_38308374/article/details/115806082

原型模式

参考:设计模式-原型模式(克隆羊多利看了都说好)_原型模式克隆羊_吾仄lo咚锵的博客-CSDN博客

public class Prototype implements Cloneable{
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    protected Object clone() {
        try {
                return super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }finally {
                return null;
            }
    }
    public static void main ( String[] args){
        Prototype pro = new Prototype();
        Prototype pro1 = (Prototype)pro.clone();
    }
}
原型模式实际上就是实现 Cloneable 接口,重写 clone ()方法。

使用原型模式的优点:

1. 性能优良
       原型模式是在内存二进制流的拷贝,要比直接new 一个对象性能好很多,特别是 要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。
2. 逃避构造函数的约束
       这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的(参见 13.4 节)。

使用场景:

        资源优化场景 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
        性能和安全要求的场景 通过new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
        一个对象多个修改者的场景 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可 以考虑使用原型模式拷贝多个对象供调用者使用。

补充:浅拷贝和深拷贝

浅拷贝: Object 类提供的方法 clone 只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝,其他的原始类型
比如 int long char string (当做是原始类型)等都会被拷贝。
注意: 使用原型模式时,引用的成员变量必须满足两个条件才不会被拷贝:一 是类的成员变量,而不是方法内变量;二是必须是一个可变的引用对象,而不是 一个原始类型或
不可变对象。
深拷贝:对私有的类变量进行独立的拷贝
如: this.arrayList = (ArrayList)this.arrayList.clone();

结构型模式

装饰模式

动态的将新功能附加到对象上。在对象功能的拓展方面,比继承更有弹性。同时装饰者模式也体现了开闭原则。

       角色分析:

        1、抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
        2、具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
        3、抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
        4、具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
 

//定义抽象类
public abstract class Drink {
    public abstract double cost();
}
//定义两个抽象类的实现类
public class Juice extends Drink{
    @Override
    public double cost() {
        System.out.println("juice: "+16);
        return 16;
    }
}
public class Milk extends Drink{
    @Override
    public double cost() {
        System.out.println("milk: "+12);
        return 12;
    }
}
//定义装饰抽象类
public abstract class Decorator extends Drink {
    public abstract double cost();
}
//定义两个装饰具体实现类
public class Chocolate extends Decorator{
    private final static double COST = 4;
    private Drink drink;
 
    public Chocolate(Drink drink) {
        this.drink = drink;
    }
 
    @Override
    public double cost() {
        System.out.println("chocolate:"+4);
        return COST+drink.cost();
    }
}
public class Pudding extends Decorator{
    private final static double COST = 5;
    private Drink drink;
 
    public Pudding(Drink drink) {
        this.drink = drink;
    }
 
    @Override
    public double cost() {
        System.out.println("pudding:"+5);
        return COST+drink.cost();
    }
}
//测试类
public class Test {
    public static void main(String[] args) {
        Drink milk = new Milk();
        milk = new Pudding(milk);
        milk = new Chocolate(milk);
        System.out.println(milk.cost());
    }
}

使用场景

1. 需要扩展一个类的功能,或给一个类增加附加功能。
2. 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
3. 需要为一批的兄弟类进行改装或加装功能,当然是首选装饰模式。

代理模式

由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

静态代理模式:

        角色分析:           

        1、抽象角色:一般会使用接口或抽象类来解决

        2、真实角色:被代理的角色

        3、代理角色:代理真实角色,代理真实角色后我们会进行一些附属操作

        4、访问角色:访问代理对象的人
 

//租房
public interface Rent {
    void rent();
}
//房东
public class Master implements Rent{
    @Override
    public void rent() {
        System.out.println("Master rent");
    }
}
//中介
public class Proxy implements Rent{
    private Master master;
 
    public Proxy() {
    }
 
    public Proxy(Master master) {
        this.master = master;
    }
 
    @Override
    public void rent() {
        see();
        master.rent();
        fare();
    }
    //看房
    public void see(){
        System.out.println("see");
    }
    //收费
    public void fare(){
        System.out.println("fare");
    }
}
//测试类
public class Consumer {
    public static void main(String[] args) {
        Master master = new Master();
        //进行代理
        Proxy proxy = new Proxy(master);
        //不需要通过对象,直接通过代理完成响应业务
        proxy.rent();
    }
}

得到测试结果

        see
        Master rent
        fare

代理模式优点:可以使真实角色的操作更加纯粹!不用去关注一些公共的业务,公共也就可以交给代理角色,实现了业务的分工,公共业务发生扩展的时候,方便集中管理!
代理模式缺点:一个真实角色就会产生一个代理角色;代码量会翻倍开发效率会变低,也许,这样无法理解到代理模式的好处。举个例子也许能更好理解,比如说我们想要在原有固定功能上新增业务,按照开闭原则我们是不能对原有代码进行修改的。但是我们可以通过代理模式,增加代理,在实现原有功能的情况下写入新的功能,创建对象时也就可以使用代理,完成操作。

动态代理模式:

虽然静态代理模式可以很好的解决开闭原则,但是每有一个真实角色就会产生一个代理,代码量翻倍过于臃肿,开发效率较低。因此,我们就使用动态代理模式进行设计。

//接口
public interface IUserService {
    void add();
    void delete();
    void update();
    void query();
 
}
//实现类
public class UserServiceImpl implements IUserService {
    @Override
    public void add() {
        System.out.println("add");
    }
 
    @Override
    public void delete() {
        System.out.println("delete");
    }
 
    @Override
    public void update() {
        System.out.println("update");
    }
 
    @Override
    public void query() {
        System.out.println("query");
    }
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//自动生成动态代理类模板
public class ProxyInvocationHandler implements InvocationHandler {
    //被代理接口
    private Object target;
   
    public void setTarget(Object target) {
        this.target = target;
    }
     //得到代理类
    public Object getProxy() {
        return Proxy.newProxyInstance(getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
    public void log(String s) {
        System.out.println("[debug]:" + s);
    }
    //得到代理类
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());
        Object result = method.invoke(target, args);
        return result;
    }
}
//测试类
public class Consumer {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();
        ProxyInvocationHandler handler = new ProxyInvocationHandler();
        //设置代理对象
        handler.setTarget(userService);
        //生成代理类
        IUserService proxy = (IUserService)handler.getProxy();
        proxy.add();
        proxy.query();
    }
}

得到测试结果

        [debug]:add
        add
        [debug]:query
        query

优点:①可以使真实角色的操作更加纯粹!不用去关注一些公共的业务。②公共也就可以交给代理角色!实现了业务的分工。③公共业务发生扩展的时候,方便集中管理。④一个动态代理类代理的是一个接口,一般就是对应的一类业务。⑤一个动态代理类可以代理多个类,只要是实现了同一个接口即可!

外观模式

是什么?

外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。使用外观模式时,我们创建了一个统一的类,用来包装子系统中一个或多个复杂的类,客户端可以直接通过外观类来调用内部子系统中方法,从而外观模式让客户和子系统之间避免了紧耦合。

角色

门面(Facade)角色:客户端调用这个角色的方法。该角色知道相关的一个或多个子系统的功能和责任,该角色会将从客户端发来的请求委派带相应的子系统中去。

子系统(subsystem)角色:可以同时包含一个或多个子系统。每个子系统都不是一个单独的类,而是一个类的集合。每个子系统都可以被客户端直接调用或被门面角色调用。对于子系统而言,门面仅仅是另外一个客户端,子系统并不知道门面的存在。

实现

/// <summary>
    /// 以学生选课系统为例子演示外观模式的使用
    /// 学生选课模块包括功能有:
    /// 验证选课的人数是否已满
    /// 通知用户课程选择成功与否
    /// 客户端代码
    /// </summary>
    class Student
    {
        private static RegistrationFacade facade = new RegistrationFacade();

        static void Main(string[] args)
        {
            if (facade.RegisterCourse("设计模式", "Learning Hard"))
            {
                Console.WriteLine("选课成功");
            }
            else
            {
                Console.WriteLine("选课失败");
            }

            Console.Read();
        }
    }

    // 外观类
    public class RegistrationFacade
    {
        private RegisterCourse registerCourse;
        private NotifyStudent notifyStu;
        public RegistrationFacade()
        {
            registerCourse = new RegisterCourse();
            notifyStu = new NotifyStudent();
        }

        public bool RegisterCourse(string courseName, string studentName)
        {
            if (!registerCourse.CheckAvailable(courseName))
            {
                return false;
            }

            return notifyStu.Notify(studentName);
        }
    }

    #region 子系统
    // 相当于子系统A
    public class RegisterCourse
    {
        public bool CheckAvailable(string courseName)
        {
            Console.WriteLine("正在验证课程 {0}是否人数已满", courseName);
            return true;
        }
    }

    // 相当于子系统B
    public class NotifyStudent
    {
        public bool Notify(string studentName)
        {
            Console.WriteLine("正在向{0}发生通知", studentName);
            return true;
        }
    }
    #endregion

外观的优缺点:

优点:

1、外观模式对客户屏蔽了子系统组件,从而简化了接口,减少了客户处理的对象数目并使子系统的使用更加简单。

2、外观模式实现了子系统与客户之间的松耦合关系,而子系统内部的功能组件是紧耦合的。松耦合使得子系统的组件变化不会影响到它的客户。

缺点:

如果增加新的子系统可能需要修改外观类或客户端的源代码,这样就违背了”开——闭原则“(不过这点也是不可避免)。

使用场景:

在以下情况下可以考虑使用外观模式:

1、外一个复杂的子系统提供一个简单的接口
2、提供子系统的独立性
3、在层次化结构中,可以使用外观模式定义系统中每一层的入口。其中三层架构就是这样的一个例子。

组合模式

定义

将对象组合成树形结构以表示 部分 - 整体 的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

实现

public class Composite extends Component {
//构件容器
private ArrayList componentArrayList = new ArrayList();
//增加一个叶子构件或树枝构件
public void add(Component component){
this.componentArrayList.add(component);
}
//删除一个叶子构件或树枝构件
public void remove(Component component){
this.componentArrayList.remove(component);
}
//获得分支下的所有叶子构件和树枝构件
public ArrayList getChildren(){
return this.componentArrayList;
}
}

使用场景

1. 维护和展示部分 - 整体关系的场景,如树形菜单、文件和文件夹管理。
2. 从一个整体中能够独立出部分模块或功能的场景。

行为型模式

观察者模式

对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。

        观察者模式有点:

        1、降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。

        2、目标与观察者之间建立了一套触发机制。
 

//定义观察者接口
public interface Observer {
    void response();
}
//具体化观察者1
public class Observer1 implements Observer{
    @Override
    public void response() {
        System.out.println("Observer1 action");
    }
}
//具体化观察者2
public class Observer2 implements Observer{
    @Override
    public void response() {
        System.out.println("Observer2 action");
    }
}
//抽象目标
public abstract class Subject {
    //创建观察者集合
    protected ArrayList<Observer> array = new ArrayList<Observer>();
    //增加观察者
    public void add(Observer observer){
        array.add(observer);
    }
    //删除观察者
    public void remove(Observer observer){
        array.remove(observer);
    }
    //通知观察者方法
    public abstract void notifyObserver();
}
//具体化目标
public class Subject1 extends Subject{
    @Override
    public void notifyObserver() {
        for (Observer observer :array){
            observer.response();
        }
    }
}
//测试类
public class Test {
    public static void main(String[] args) {
        Subject subject = new Subject1();
        Observer obs1 = new Observer1();
        Observer obs2 = new Observer2();
        subject.add(obs1);
        subject.add(obs2);
        subject.notifyObserver();
    }
}

得到测试结果:

        Observer1 action
        Observer2 action

使用场景

1. 关联行为场景。需要注意的是,关联行为是可拆分的,而不是 组合 关系。
2. 事件多级触发场景。
3. 跨系统的消息交换场景,如消息队列的处理机制

责任链模式

是什么

一种处理请求的模式,它让多个处理器都有机会处理该诘求,直到其中某个处理成功为止。责任链模式把多个处理器串成链,然后让请求在链上传递。


使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。

责任链模式的主要角色

        抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
        具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
        客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

代码实现

//抽象处理者
public abstract class Handler {
    private Handler next;
    public void setNext(Handler next) { this.next = next; }
    public Handler getNext() { return next; }
 
    //处理请求
    public abstract void handleRequest(int info);
}
//具体处理者1
public class Handler1 extends Handler{
    @Override
    public void handleRequest(int info) {
        if (info <10){
            System.out.println("Handler1完成处理");
        }else {
            if (getNext()!=null){
                getNext().handleRequest(info);
            }else {
                System.out.println("没有处理者进行处理");
            }
        }
    }
}
//具体处理者2
public class Handler2 extends Handler{
    @Override
    public void handleRequest(int info) {
        if (info <20&&info>10){
            System.out.println("Handler2完成处理");
        }else {
            if (getNext()!=null){
                getNext().handleRequest(info);
            }else {
                System.out.println("没有处理者进行处理");
            }
        }
    }
}
测试类
public class Test {
    public static void main(String[] args) {
        Handler handler1 = new Handler1();
        Handler handler2 = new Handler2();
        handler1.setNext(handler2);
        handler1.handleRequest(5);
        handler1.handleRequest(15);
        handler1.handleRequest(25);
    }
}

得到测试结果:

Handler1完成处理
Handler2完成处理
没有处理者进行处理

优缺点

        责任链模式优点
                降低了对象之间的耦合度。处理者不需要知道客户的任何信息,客户也不要知道处理者是如何实现方法的。
                提高了系统的灵活性。当我们想要新增处理器到整个链条中时,所付出的代价是非常小的
        责任链模式缺点
                降低了系统的性能。对比较长的职责链,请求的处理可能涉及多个处理对象
                不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值