大话设计模式(更新ing...)

本文介绍了设计模式中的单例模式、简单工厂模式和工厂方法模式,详细讲解了各种实现方式及其优缺点。单例模式确保一个类只有一个实例,防止内存浪费。简单工厂模式简化了对象的创建,但可能导致类的职责不清。工厂方法模式将对象的创建交给子类处理,提高灵活性。此外,文章还探讨了这些模式在实际开发中的应用场景和注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

单例模式:

简单工厂模式

工厂方法模式

抽象工厂模式

策略模式

观察者模式

适配器模式

模板方法模式(模板模式)

装饰者模式

静态代理模式

动态代理模式

责任链模式

享元模式

迭代器模式

桥接模式


单例模式:

方式一:拿去吧!

如果一个对象被new了多次,但无法保证每个对象都会被使用,这时候就会很浪费空间,而且如果访问量巨大,服务器也要承担很大的压力。出现这种情况,一些Java大神们当然不会放任着不管,于是经过一番潜心研究,就出现了一种新的获得对象的方式——单例模式。

单例模式大概做的事情就是通过自己的设计,让当前的这个类只能产生一个对象(但是根据特定情况以及自己对于这一块的设计,不能一刀切的都去用这个方法,比如一个person类,每个person都有自己不同的属性,怎么能都用一个person来做事情呢)。

如果做?

  1. 保证当前类的构造方法不能被随便调用吧,这是最基本的条件。也就是让构造方法私有化--private XXX(){}
  2. 不能调用构造方法,那我怎么获取这个对象呢。有四种方式: 
    1. 构造方法
    2. 代码块
    3. 普通方法
    4. 属性

构造方法:前面我们为了不能随便创建当前对象,已经给构造方法私有化,所以排除此方法。

代码块:代码块会在对象创建的时候就加载了,但是没有返回值,同样获取不到对象,排除。

普通方法:这个似乎和构造方法一样了,每次调用这个方法同样会创建一个对象出来,也无法做到对象时单例的。

属性: 似乎只能使用属性来new对象,然后通过方法来将对象传递。

                public Singleton single = new Singleton();

上面这个写法很明显是不对的,而且在做单例上面,似乎没有任何存在的意义。

原因是啥呢?

首先,这样写上来就是一个StackOverflow Error。因为这是类里面的属性,类加载以后会加载自己里面的属性,方法等。但是当加载这个属性的时候,发现又new了一个自己,然后又去加载自己类,又遇到一个属性里面new了一个自己。。。陷入死循环导致栈溢出。

解决方法:保证是一份就OK啦。 public static Singleton single = new Singleton();

有些朋友可能看出还有一个问题:就是这样写我岂不是用类名就可以随便调用了,Singleton.single简直不要太简单。所以呢,为了让我的获取对象的方法更有存在感,必然是不能让您调用的嗷。

较为标准的写法:private static Singleton single = new Singleton();

好啦。属性对象创建好了,我怎么给你呢--->通过方法获得。 设计一个方法,将当前唯一的对象返回出去。       

public static Singleton getInstance(){

     //注意:这里return的是single属性中存储对象的地址引用

     return single;

}

这种方式又叫做饿汉式,也就是:上来就创建一个对象,如同饿汉见到食物上来就吃。但是呢,这样在整个系统执行过程中,如果创建的对象不使用,就会一直占用着内存,造成了内存空间的浪费。


方式二:不用,我就不做!

这种方式呢,又叫做懒汉式,就是你可以要,但是我未必会创建,等到你用的时候我再创建,这样就很大程度上避免了内存空间浪费问题。写法和饿汉式几乎一致,代码如下:

private Singleton(){}
//我先不创建对象
private static Singleton single;
public static Singleton getInstance(){
    //这个时候你要用了,但是我要看看是否真的没有创建好的对象吗,没有我再给你
    if(single == null){
        single = new Singleton();
    }
    return single;
}

有利肯定有弊:这时候要考虑到线程的问题了,比如A和B两个线程,同时需要这个对象,假如A先开辟了一块空间single,A正准备判断对象是否为空,这个时候时间片轮转,到B了,B也开辟了一块空间,然后A又创建了对象,但此时B判断对象已经不为空了,但是自己又开辟了一块空间,就比较冲突。当线程数更多的话,资源的争夺就会更明显。所以,请看下个分解。


方式三:我给锁住,让你抢我的东西!

在懒汉式的基础上加一个锁,就是我在用这个对象的时候,我要给他锁住,你们谁也不能动,直到我用完。代码实现如下:

private Singelton(){}
private static Singleton single;
//当前线程使用的时候不允许其他线程操作
public synchronized static Singleton getInstance(){
    if(single == null){
        single = new Singleton();
    }
    return single;
}

有利有弊:这种整个方法的执行过程中,不能有其他线程操作的方式,线程安全是解决了,万一你用很久,谁等的起啊。


方式四:双重判定!

就是将当前的类模板锁住,而不是锁住整个对象,这样其他线程进来如果对象不是空的,就可以直接返回了,而不需要去等待加锁的线程执行完毕,这样大大提高了执行效率。实现如下:

private Singelton(){}
private static Singleton single;
public static Singleton getInstance(){
    //第一重判定,如果为空,加锁,创建对象
    if(single == null){
        synchronized(Singleton.class){
            //第二重判定,如果为空 创建对象
            if(single == null){
                single = new Singleton();
            }
        }
    }
    return single;
}

有利有弊:这样在解决了加锁性能的问题的同时,在JVM虚拟机中,在对象的开辟和赋值的过程中,可能会产生指令重排序。本来时1 ——> 2 ——> 3的顺序,可能会变成 1 ——> 3 ——> 2。

  1. 为 instance分配内存空间。
  2. 初始化 instance
  3. 将 instance指向分配的内存地址。

方式五:大哥来了!

属性上添加volatile,保证属性在加载和赋值的过程中,不会被JVM指令重排序。代码如下:

private Singleton single(){}
private static volatile Singleton single;
public static Singleton getInstance(){
    if(single == null){
        synchronized(Singleton.class){
            if(single == null){
                single = new Singleton();
            }
        }

    }
}

简单工厂模式

大家所熟知的有工厂方法模式、抽象工厂模式,那么简单工厂模式又是什么呢?简单工厂模式不能算作一个标准的设计模式,但是呢,在开发之中又挺常用的,那么就当做一个热身吧!

举一个小案例:话说有一天晚上,博主突发饿疾,非要去附近的小作坊买一些关东煮吃吃。且附近几家都有卖。那么问题来了,我新来的这个地方,还不熟悉,我还没吃过这附近的关东煮呢,万一关东煮里面有其他东西,出了问题,我找谁去呢??找我买东西的那家店?万一人家无证经营跑路了呢。所以为了安全起见,我需要看到人家的营业执照才放心的买。那么这就要求这些小作坊需要被统一管理起来(颁发营业执照,你这家店可以卖关东煮),或者说的贴切代码一些(遵循一个规则)。

好,假设有三家小作坊店,为了方便理解,类名就用拼音了(其实是我不会英语)。那么:GuanDongZhu1.java    GuanDongZhu2.java    GuanDongZhu3.java。这是我附近的三家店。现在我要给他们指定一个规则,允许他们卖关东煮。AcceptGuanDongZhu.java。既然是指定规则的类,我们就可以将它指定为借口或者父类。此例中将其指定为借口。那么三个小作坊店都实现这个借口里面的方法(卖关东煮)。代码如下:

GuanDongZhu1.java

public class GuanDongZhu1 implements AcceptGuanDongZhu{
    @Override
    public void sale() {
        System.out.println("GuanDongZhu1号店也售卖关东煮,博主快来吃啊");
    }
}

GuanDongZhu2.java

public class GuanDongZhu2 implements AcceptGuanDongZhu{
    @Override
    public void sale() {
        System.out.println("GuanDongZhu2号店售卖关东煮,博主快来吃啊");
    }
}

GuanDongZhu3.java

public class GuanDongZhu3 implements AcceptGuanDongZhu{
    @Override
    public void sale() {
        System.out.println("GuanDongZhu3号店关东煮最好吃,博主快来吃啊");
    }
}

AcceptGuanDongZhu.java

public interface AcceptGuanDongZhu {
    //售卖关东煮的接口方法(营业执照)  默认用 public abstract修饰
    void sale();
}

TestMain.java

public static void main(String[] args) {
        //多态来创建子类对象
        AcceptGuanDongZhu sale = new GuanDongZhu1();
        sale.sale();
}

好,营业执照给了,小作坊也遵循了,但是!新的问题来了。用户这样用,是不是知道的太多了。首先,他知道了我的接口规则(实现了AcceptGuanDongZhu这个接口),其次,我具体的实现类也让他知道了,这完全违背封装隔离的思想啊。那么我就需要把用户和店隔开,用户可以看见我的实现规则,但是呢,我不能让你知道我的具体实现类是哪个,你可以通过传参数的方式,告知我想去哪个店,我给你找。而中间这个隔离层呢,就是我们今天的重点了——Factory。就是你告诉我工厂,你要买关东煮了,如果你传给我一个参数(店名),那我就把这个店给到你,不传,我就随便给你一个。按照这种封装隔离的思想,把具体的实现类包装起来。代码实现如下:

GuanDongZhuFactory.java

//调用我工厂 我要给你返回一个店  你给我传一个店名 或者不传我就随机给你一个
    public AcceptGuanDongZhu getGuanDongZhu(String name){
        //下面就简单做个判断,重要的是思想
        if(!"".equals(name) && "GuanDongZhu1".equals(name)){
            return new GuanDongZhu1();
        }else if(!"".equals(name) && "GuanDongZhu2".equals(name)){
            return new GuanDongZhu2();
        }else if(!"".equals(name) && "GuanDongZhu3".equals(name)){
            return new GuanDongZhu3();
        }else{
            //啥也不传,我给你选一个
            return new GuanDongZhu1();
        }
    }

那么这时候用户找店的方式就应该变成:

TestMain.java

public static void main(String[] args) {
        //先创建工厂
        GuanDongZhuFactory factory = new GuanDongZhuFactory();
        AcceptGuanDongZhu sale = factory.getGuanDongZhu("GuanDongZhu1");
        sale.sale();
    }

工厂方法模式

引子:为什么会有工厂方法模式?

在上面的简单工厂模式中,用户通过创建工厂对象来获取目标对象(GuanDongZhu对象),那么由于关东煮不止一家,用户肯定有自己想去的一家,在简单工厂模式中依靠传参数来决定选择哪一家,那这就意味着用户需要了解参数的意义。而且啊,添加了参数,我工厂处理的时候岂不是多了很多的判断,如:下面就是简单工厂中的工厂要做的事情。

if(!"".equals(name) && "GuanDongZhu1".equals(name)){
            return new GuanDongZhu1();
        }else if(!"".equals(name) && "GuanDongZhu2".equals(name)){
            return new GuanDongZhu2();
        }else if(!"".equals(name) && "GuanDongZhu3".equals(name)){
            return new GuanDongZhu3();
        }else{
            //啥也不传,我给你选一个
            return new GuanDongZhu1();
        }

这样,每新开一家店,大工厂就要多一个判断,是不是挺麻烦啊。可是不判断呢,我又不知道该创建哪个具体的对象,唉,好烦,不干了!对,不干了——无为而治。那怎么办?这样吧,你大工厂把自己的方法变成抽象的,然后我再找人去专门创建对象,但是呢,你大工厂要把我创建好的对象给人家用户好吧。“好!”。OK,大工厂的获取对象的方法变成抽象了,那类也就是抽象的了。创建对象的事,我再找人,小弟多。我给每一个关东煮店都配一个创建对象的小弟(小工厂),然后由大工厂去把这个小工厂找过来递给用户。部分代码实现如下:

GuanDongZhuFactory.java   总工厂类

public abstract class GuanDongZhuFactory {
    //判断太麻烦 不干了! 你再找小弟去创建对象吧,我只负责给你找小弟,然后送给用户
    public abstract AcceptGuanDongZhu chooseGuanDongZhu();
    //为了不让用户知道  我大工厂又去找小工厂干活了 掩盖住我懒惰的事实
    public AcceptGuanDongZhu getGuanDongZhu(){
        return chooseGuanDongZhu();
    }
}

GuanDongZhu1Factory.java   每个店的小工厂(负责创建对象)

public class GuanDongZhu1Factory extends GuanDongZhuFactory{
    //一号店的小弟 创建了一号店对象
    @Override
    public AcceptGuanDongZhu chooseGuanDongZhu() {
        return new GuanDongZhu1();
    }
}

TestMain.java

public static void main(String[] args) {
        //大工厂拿着小工厂给创建好的对象 借花献佛  送给了用户  卖关东煮成功
        GuanDongZhuFactory factory = new GuanDongZhu1Factory();
        AcceptGuanDongZhu sale = factory.getGuanDongZhu();
        sale.sale();
    }

给每一家店都分配一个小工厂,这个小工厂只创建这一家的对象,就省去了判断的步骤,用户直接创建对应的那一家店,这样只暴露给用户工厂类,具体实现类用户是不知道的。

所谓工厂方法,中的方法指的就是总工厂里面的抽象方法,小工厂通过继承大工厂,然后重写这个方法实现对象创建。 工厂方法模式中的 “ 方法 ”。

    public abstract AcceptGuanDongZhu chooseGuanDongZhu();

抽象工厂模式

模拟计算机组件(主板,CPU)来描述此模式,(相关知识可能有误,见谅)先实现没有抽象工厂模式,只是简单工厂模式,将两者进行对比。

简单工厂模式实现如下:

AMDCPU.java

public class AMDCPU implements CPU {
    //属性  名字
    private String name;
    //CPU的管脚数
    private int basePins;

    public AMDCPU(String name, int basePins) {
        this.name = name;
        this.basePins = basePins;
    }
    //CPU用户核心计算  实现一个接口 CPU  统一进行计算
    @Override
    public void centerCalculate() {
        System.out.println("这是AMD的CPU" + this.name);
    }
}
InterCPU.java
public class InterCPU implements CPU{
    private String name;
    private int basePins;
    public InterCPU(String name,int basePins){
        this.name = name;
        this.basePins = basePins;
    }
    //CPU用户核心计算  实现一个接口  进行统一管理
    @Override
    public void centerCalculate() {
        System.out.println("这是Inter的CPU" + this.name);
    }
}

CPU.java  统一管理的接口

    void centerCalculate();

CPUFactory.java  创建对象的简单工厂

//默认  1  是  Inter   2  是  AMD
    public static CPU createCPU(int type){
        if(type == 1){
            return new InterCPU("Inter",1155);
        }else{
            return new AMDCPU("AMD",775);
        }
    }

ASUSMainBoard.java  


                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值