讲解设计模式之(单例/工厂/代理/模板)模式

设计模式

设计模式(Design Patterns)是软件开发人员在软件开发过程中面临的一般问题的解决方案。设计模式不是一种工具或产品,而是一种描述在特定环境下,如何解决问题或设计软件的思路和方法的模板。

1.1 设计模式的必要性

设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。

正确使用设计模式具有以下优点。

  • 可以提高程序员的思维能力、编程能力和设计能力。
  • 使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
  • 使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。
1.2 设计模式的分类

Java 中一般认为有 23 种设计模式,我们不需要所有的都会,但是其中常用的几种设计模式应该去掌握。下面列

出了所有的设计模式。需要掌握的设计模式我单独列出来了,当然能掌握的越多越好。

总体来说设计模式分为三大类:

创建型模式,共五种:工厂方法模式抽象工厂模式单例模式建造者模式、原型模式。

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

行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模

式、状态模式、访问者模式、中介者模式、解释器模式。

1.3 单例模式

单例模式的主要有以下角色:

  • 单例类。只能创建一个实例的类.
  • 访问类。使用单例类
1.3.1 饿汉式
class Student{
    //构造私有化
    private Student(){

    }

    //创建静态本类对象---饿汉式
    //private static Student stu=new Student();

    //提供静态方法---返回本类对象---饿汉式
    public static Student Singleton(){
        return stu;
    }
}

说明:

​ 该方式在成员位置声明Singleton类型的静态变量,并创建Singleton类的对象instance。instance对象是随着类的加载而创建的。如果该对象足够大的话,而一直没有使用就会造成内存的浪费。

1.3.2 懒汉式(双重检查锁)
class Student{
    //构造私有化
    private Student(){

    }
    //创建静态本类对象---懒汉式
    private static Student stu;

    //提供静态方法---返回本类对象---懒汉式
    public static Student Singleton(){
        //第一次判断,多线程进入;后续线程不需要进入直接返回
        if (stu==null){
            //多线程抢锁,解决线程安全
            synchronized (Student.class){
                //第一次抢到锁的线程,创建对象,同时第一次其余多线抢到锁后不需要创建
                if (stu==null){
                    stu=new Student();
                }
            }
        }
        return stu;
    }
}
双重检查锁模式是一种非常好的单例实现模式,解决了单例、性能、线程安全问题,上面的双重检测锁模式看上去完美无缺,其实是存在问题,在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。

要解决双重检查锁模式带来空指针异常的问题,只需要使用 `volatile` 关键字, `volatile` 关键字可以保证可见性和有序性。(在成员属性上加上volatile关键字)
1.4 工厂模式

工厂模式(Factory Pattern) 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

简单来说就是: 我们创建一个工厂类,然后当调用者想要创建一个对象时,只需要告诉工厂类即可,由工厂类去创建对象,调用者无需知道是如何创建的,也不用自己去创建。

再形象点说就是: 比如我们需要购买一批电脑,我们只要把我们的需求告诉电脑工厂,电脑工厂就会帮我们把这批电脑做好,而我们是不用自己去做这个电脑的,也不用我们自己去知道这个电脑是怎么做出来的,这就是工厂模式。

工厂模式分为简单工厂模式工厂方法模式抽象工厂模式,它们都属于设计模式中的创建型模式。其主要功能都是帮助我们把对象的实例化部分抽取了出来,目的是降低系统中代码耦合度,并且增强了系统的扩展性。

1.4.1 简单工厂模式

简单工厂模式只是将不同对象的创建操作进行了一层简单的封装,其实也就是把不同对象的创建操作全都单独放到一个类中,这个类就成为了简单工厂类;当我们需要某个对象时,只需把我们的需求告诉这个简单工厂类,然后由这个简单工厂类根据我们的需求去创建对应的对象即可。

1、创建手机抽象类

public abstract class Phone {

    public abstract void lookTime();
    public abstract void play();

}

2、创建继承了手机抽象类的,具体的不同品牌的手机的实体类

public class HuaweiPhone extends Phone{
    @Override
    public void lookTime() {
        System.out.println("华为");
    }
    @Override
    public void play() {
        System.out.println("打游戏");
    }
}


public class VivoPhone extends Phone{
    @Override
    public void lookTime() {
        System.out.println("vivo");
    }
    @Override
    public void play() {
        System.out.println("vivo玩");
    }
}

3、 创建一个手机工厂

public class PhoneFactory {

    public static Phone createPhone(String type){
        if (type.equals("Huawei")){
            return new HuaweiPhone();
        } else if (type.equals("vivo")) {
            return new VivoPhone();
        }

        return null;
    }

}

4、测试:使用工厂生产不同品牌手机并使用

public class Test {
    public static void main(String[] args) {
        Phone phone=PhoneFactory.createPhone("Huawei");
        phone.lookTime();
    }
}

缺点: 扩展性差,违背了开闭原则(开闭原则指的是:软件实现应该对扩展开放,对修改关闭)。新增产品时,需要修改工厂类。

1.4.2 工厂方法模式

在工厂方法模式中,我们的工厂类下面还有很多子工厂类,我们需要的对象是由这些子工厂类来创建的。其实就是改进了简单工厂模式,因为当我们需要一个新产品时,只需要扩展一个新的子工厂类即可,而不用去修改原有的代码,这样就符合了开闭原则。

1、创建一个手机抽象类(同上)

2、创建继承了手机抽象类的,具体的不同品牌的手机的实体类(同上)

3、创建一个手机抽象工厂类

public abstract class AbstractFactory {
    public abstract Phone createPhone();

}

4、创建手机抽象工厂类的子类(根据不同的手机品牌)

public class huaweiFactory extends AbstractFactory{
    @Override
    public Phone createPhone() {
        return new huawei();
    }
}


public class vivoFactory extends AbstractFactory{
    @Override
    public Phone createPhone() {

        return new vivo();
    }
}

5、测试:使用不同子工厂生产不同品牌电脑并使用

public class Test01 {
    public static void main(String[] args) {
        Phone phone=new huaweiFactory().createPhone();
        phone.play();
        Phone phone1=new vivoFactory().createPhone();
        phone1.play();
    }
}

优点: 扩展性好,符合了开闭原则,新增一种产品时,只需增加改对应的产品类和对应的工厂子类即可。比如样例实现中,当我们需要一个苹果电脑时,只需要去新增一个苹果电脑类和一个苹果工厂类即可,而无需去修改原有的代码。符合单一职责原则,每个工厂只负责一种产品,而不是由一个工厂去生成所有商品。

缺点: 当我们新增产品时,还需要提供对应的工厂类,系统中类的个数将会成倍增加,相当于增加了系统的复杂性

1.4.3 抽象工厂模式

1、 创建各种类型的手机抽象类

public abstract class Phone {
    abstract void use();
}

public abstract class miniPhone {
    abstract void use();
}

2、 创建具体的不同品牌的各种类型的手机实体类

public class huawei extends Phone{
    @Override
    void use() {
        System.out.println("正常华为手机");
    }
}

public class minihuawei extends miniPhone{
    @Override
    void use() {
        System.out.println("华为迷你手机");
    }
}

public class vivo extends Phone{
    @Override
    void use() {
        System.out.println("vivo正常手机");
    }
}

public class minivivo extends miniPhone{
    @Override
    void use() {
        System.out.println("vivo迷你");
    }
}

3、创建一个手机的抽象工厂类

public abstract class AbatractPhoneFactory {
    public abstract Phone producePhone();
    public abstract miniPhone produceminiPhone();
}

4、 创建手机抽象工厂类的子类(根据不同的手机品牌)

public class huaweiFactory extends AbatractPhoneFactory{
    @Override
    public Phone producePhone() {
        return new huawei();
    }

    @Override
    public miniPhone produceminiPhone() {
        return new minihuawei();
    }
}

public class vivoFactory extends AbatractPhoneFactory{
    @Override
    public Phone producePhone() {
        return new vivo();
    }

    @Override
    public miniPhone produceminiPhone() {
        return new minivivo();
    }
}

5、 测试:使用不同子工厂创建不同品牌的不同种类的手机

public class Test01 {
    public static void main(String[] args) {
        AbatractPhoneFactory abatractPhoneFactory=new huaweiFactory();
        Phone phone = abatractPhoneFactory.producePhone();
        phone.use();
        miniPhone miniPhone = abatractPhoneFactory.produceminiPhone();
        miniPhone.use();
    }
}
//运行结果
正常华为手机
华为迷你手机
1.5 代理模式

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

考虑生活中一个常见的例子,客户想买房,房东有很多房,提供卖房服务,但房东不会带客户看房,于是客户通过中介买房。

代理模式的结构

代理模式的结构比较简单,主要是通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问,下面来分析其基本结构。

代理模式的主要角色如下。

  1. 抽象主题(Subject)类(业务接口类):通过接口或抽象类声明真实主题和代理对象实现的业务方法,服务端需要实现该方法。
  2. 真实主题(Real Subject)类(业务实现类):实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
  3. 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
1.5.1 静态代理

静态代理在使用的时候,需要定义接口或者父类,被代理对象(目标对象)与代理对象一起实现共同的接口或者是继承相同的父类。

//接口方法--卖房子
public interface RentHouse {

    public void RentHouse();
}
//真实主题--房东
public class Landlord implements RentHouse{
    @Override
    public void RentHouse() {
        System.out.println("看房子");

    }
}
//代理类--中介
public class Agent implements RentHouse{

    private Landlord landlord;

    public Agent(Landlord l){
        this.landlord=l;
    }
    @Override
    public void RentHouse() {
        System.out.println("请客户喝茶");
        landlord.RentHouse();
        System.out.println("送走客户");
    }
}

优点:符合开闭原则,能对目标对象进行扩展。

缺点:为每一个需要增强的服务都需要创建类,

1.5.2 动态代理

动态代理基本介绍:

1)代理对象不需要实现接口,但是目标对象要实现接口,否则不能使用
2)代理对象的生成,是利用jdk的api,动态的在内存中构建代理对象
3)动态代理也叫作:jdk代理,接口代理

//接口
public interface  RentHouse {

    public void RentHouse();
}
//真实
public class Landlord implements RentHouse {
    @Override
    public void RentHouse() {
        System.out.println("看房子");

    }
}
//代理对象
public class JDKProxy {
    private RentHouse rentHouse;

    public  JDKProxy(RentHouse rentHouse){
        this.rentHouse=rentHouse;
    }
    public Object getProxy(){
        //当前对象使用的目标类加载器
        ClassLoader classLoader = rentHouse.getClass().getClassLoader();
        //目标对象实现的接口类型,使用泛型的方式确认类型
        Class<?>[] interfaces = rentHouse.getClass().getInterfaces();
         //执行目标对象的方法时,会出发事情处理方法,会把当前执行的对象当做参数传入
        InvocationHandler h=((proxy, method, args) -> {
            System.out.println("问候客户");
            Object invoke = method.invoke(rentHouse);
            System.out.println("答谢客户");
            return invoke;
        });
        Object o = Proxy.newProxyInstance(classLoader, interfaces, h);
        return o;
    }
}

优点:动态代理大大减少了我们的开发任务,同时减少了业务接口依赖。

缺点:始终无法摆脱interface代理的桎梏,无法实现对class的动态代理。

1.5.3 Cglib代理

前提条件:

  • 需要引入cglib的jar文件,由于Spring的核心包中已经包括了Cglib功能,所以也可以直接引入spring-core-3.2.5.jar
  • 目标类不能为final
  • 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
/**
 * 目标对象,没有实现任何接口
 */
public class Singer{

    public void sing() {
        System.out.println("唱一首歌");
    }
}
/**
 * Cglib子类代理工厂
 */
public class ProxyFactory implements MethodInterceptor{
    // 维护目标对象
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    // 给目标对象创建一个代理对象
    public Object getProxyInstance(){//1.工具类
        Enhancer en = new Enhancer();
        //2.设置父类---cglib---
        en.setSuperclass(target.getClass());
        //3.设置回调函数
        en.setCallback(this);
        //4.创建子类(代理对象)
        return en.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("向观众问好");
        //执行目标对象的方法
        Object returnValue = method.invoke(target, args);
        System.out.println("谢谢大家");
        return returnValue;
    }
}

这里的代码也非常固定,只有部分是需要自己写出

测试

/**
 * 测试类
 */
public class Test{
    public static void main(String[] args){
        //目标对象
        Singer target = new Singer();
        //代理对象
        Singer proxy = (Singer)new ProxyFactory(target).getProxyInstance();
        //执行代理对象的方法
        proxy.sing();
    }
}
1.6 模板方法模式

1)模板方法模式,在一个抽象类中公开定义了它的方法执行的模板,它的子类可以按需重写方法实现,但调用将以抽象类中定义的方式执行。

2)简单地说,模板方法模式定义一个操作中的算法骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重新定义该算法的某些特定步骤。

3)这种类型的设计模式属于行为模式。

//发布预习资料 → 制作课件PPT → 在线直播 → 提交课堂笔记 → 提交源码 → 布置作业 → 检查作业。首先创建AbastractCourse抽象类。
public abstract class AbastractCourse {
    public final void createCourse(){
        //1.发布预习资料
        postPreResoucse();
        //2.制作课件PPT
        createPPT();
        //3.在线直播
        liveVideo();
        //4.上传课后资料
        postResource();
        //5.布置作业
        postHomework();
        if(needCheckHomework()){
            checkHomework();
        }
    }
    protected abstract void checkHomework();

    //钩子方法
    protected boolean needCheckHomework(){return  false;}

    protected void postHomework(){
        System.out.println("布置作业");
    }

    protected void postResource(){
        System.out.println("上传课后资料");
    }

    protected void liveVideo(){
        System.out.println("直播授课");
    }

    protected void createPPT(){
        System.out.println("制作课件");
    }

    protected void postPreResoucse(){
        System.out.println("发布预习资料");
    }

}

//设计钩子方法的主要目的是干预执行流程,使得控制行为流程更加灵活,更符合实际业务的需求。钩子方法的返回值一般为适合条件分支语句的返回值(如boolean、int等)。

//创建java课程类
public class JavaCourse extends AbastractCourse {
    private boolean needCheckHomework = false;

    public void setNeedCheckHomework(boolean needCheckHomework) {
        this.needCheckHomework = needCheckHomework;
    }

    @Override
    protected boolean needCheckHomework() {
        return this.needCheckHomework;
    }

    protected void checkHomework() {
        System.out.println("检查Java作业");
    }
}

//测试
 public static void main(String[] args) {
        System.out.println("=========架构师课程=========");
        JavaCourse java = new JavaCourse();
        java.setNeedCheckHomework(false);
        java.createCourse();
    }
  • 25
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值