常用的设计模式

单例模式

简单点说,就是一个应用程序中,某个类的实例对象只有一个,你没有办法去new,因为构造器是被private修饰的,一般通过getInstance()的方法来获取它们的实例。getInstance()的返回值是一个对象的引用,并不是一个新的实例,所以不要错误的理解成多个对象。单例模式实现起来也很容易,直接看demo吧

public class Singleton {

       public staticvoid main(String[] args)

    {

       //创建Singleton对象不能通过构造器,只能通过getInstance方法

       Singleton s1 = Singleton.getInstance();

       Singleton s2 = Singleton.getInstance();

       //将输出true

       System.out.println(s1 == s2);

    }

   
    //使用一个变量来缓存曾经创建的实例

    private static Singleton instance;

    //将构造器使用private修饰,隐藏该构造器

    private Singleton(){

       System.out.println("Singleton被构造!");

    }

   
    //提供一个静态方法,用于返回Singleton实例

    //该方法可以加入自定义的控制,保证只产生一个Singleton对象

    publicstatic Singleton getInstance()

    {

       //如果instance为null,表明还不曾创建Singleton对象

       //如果instance不为null,则表明已经创建了Singleton对象,将不会执行该方法

       if (instance == null)

       {

           //创建一个Singleton对象,并将其缓存起来

           instance = new Singleton();

       }

       return instance;

    }

}

单例模式主要有如下两个优势:

1)      减少创建Java实例所带来的系统开销

2)      便于系统跟踪单个Java实例的生命周期、实例状态等。

 

实现方式:

 

a) 将被实现的类的构造方法设计成private的。

b) 添加此类引用的静态成员变量,并为其实例化。

c)  在被实现的类中提供公共的getInstance函数,返回实例化的此类,就是b中的静态成员变量。

适用场景: 
    单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如: 
    1.需要频繁实例化然后销毁的对象。 
    2.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。 
    3.有状态的工具类对象。 

    4.频繁访问数据库或文件的对象。 

 

 

 代理模式(Proxy)

代理模式是一种应用非常广泛的设计模式,当客户端代码需要调用某个对象时,客户端实际上不关心是否准确得到该对象,它只要一个能提供该功能的对象即可,此时我们就可返回该对象的代理(Proxy)。

代理就是一个Java对象代表另一个Java对象来采取行动。如:

public class ImageProxy implements Image

{

    //组合一个image实例,作为被代理的对象

    private Image image;

    //使用抽象实体来初始化代理对象

    public ImageProxy(Image image)

    {

       this.image = image;

    }

    /**

     * 重写Image接口的show()方法

     * 该方法用于控制对被代理对象的访问,

     * 并根据需要负责创建和删除被代理对象

     */

    public void show()

    {

       //只有当真正需要调用image的show方法时才创建被代理对象

       if (image == null)

       {
           image = new BigImage();
       }

       image.show();

    }

}

调用时,先不创建:

Image image = new ImageProxy(null);

hibernate默认启用延迟加载,当系统加载A实体时,A实体关联的B实体并未被加载出来,A实体所关联的B实体全部是代理对象——只有等到A实体真正需要访问B实体时,系统才会去数据库里抓取B实体所对应的记录。

借助于Java提供的Proxy和InvocationHandler,可以实现在运行时生成动态代理的功能,而动态代理对象就可以作为目标对象使用,而且增强了目标对象的功能。如:

Panther

public interface Panther

{
    //info方法声明

    publicvoid info();

    //run方法声明

    publicvoid run();

}

GunPanther

public class GunPanther implements Panther

{
    //info方法实现,仅仅打印一个字符串
    publicvoid info()
    {
       System.out.println("我是一只猎豹!");
    }

    //run方法实现,仅仅打印一个字符串

    publicvoid run()
    {
       System.out.println("我奔跑迅速");
    }
}

MyProxyFactory,创建代理对象

 

public class MyProxyFactory{
    //为指定target生成动态代理对象

    publicstatic Object getProxy(Object target)  throws Exception {
       //创建一个MyInvokationHandler对象

       MyInvokationHandler handler =

           new MyInvokationHandler();

       //为MyInvokationHandler设置target对象

       handler.setTarget(target);

       //创建、并返回一个动态代理

       return Proxy.newProxyInstance(target.getClass().getClassLoader()

           , target.getClass().getInterfaces(), handler);

    }

}

MyInvokationHandler,增强代理的功能

public class MyInvokationHandler implements InvocationHandler{
    //需要被代理的对象
    private Object target;
    publicvoid setTarget(Object target)  {
     this.target = target;
    }

    //执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法

    public Object invoke(Object proxy, Method method, Object[] args)

       throws Exception

    {

       TxUtil tx = new TxUtil();

       //执行TxUtil对象中的beginTx。

       tx.beginTx();

       //以target作为主调来执行method方法

       Object result = method.invoke(target , args);

       //执行TxUtil对象中的endTx。

       tx.endTx();

       return result;

    }

}

TxUtil

public class TxUtil
{

    //第一个拦截器方法:模拟事务开始

    publicvoid beginTx()

    {

       System.out.println("=====模拟开始事务=====");

    }

    //第二个拦截器方法:模拟事务结束

    publicvoid endTx()

    {

       System.out.println("=====模拟结束事务=====");

    }

}

测试

 

 public static void main(String[] args) throws Exception
    {
       //创建一个原始的GunDog对象,作为target

       Panther target = new GunPanther();

       //以指定的target来创建动态代理

       Panther panther = (Panther)MyProxyFactory.getProxy(target);

       //调用代理对象的info()和run()方法

       panther.info();

       panther.run();

    }

spring所创建的AOP代理就是这种动态代理。但是Spring AOP更灵活。

 

一)静态代理

实现方式:

a) 为真实类和代理类提供的公共接口或抽象类。(租房)

b) 真实类,具体实现逻辑,实现或继承a。(房主向外租房)

c)  代理类,实现或继承a,有对b的引用,调用真实类的具体实现。(中介)

d) 客户端,调用代理类实现对真实类的调用。(租客租房)

二)动态代理

实现方式:

a) 公共的接口(必须是接口,因为Proxy类的newproxyinstance方法的第二参数必须是个接口类型的Class)

b) 多个真实类,具体实现的业务逻辑。

c)  代理类,实现InvocationHandler接口,提供Object成员变量,和Set方法,便于客户端切换。

d) 客户端,获得代理类的实例,为object实例赋值,调用Proxy.newproxyinstance方法在程序运行时生成继承公共接口的实例,调用相应方法,此时方法的执行由代理类实现的Invoke方法接管。

jdk动态代理使用的局限性

通过反射类ProxyInvocationHandler回调接口实现的jdk动态代理,要求委托类必须实现一个接口,但事实上并不是所有类都有接口,对于没有实现接口的类,便无法使用该方方式实现动态代理。

工厂模式

模式的问题:你如何能轻松方便地构造对象实例,而不必关心构造对象实例的细节和复杂过程
解决方案:建立一个工厂来创建对象
实现:
一、引言
    1)还没有工厂时代:假如还没有工业革命,如果一个客户要一款宝马车,一般的做法是客户去创建一款宝马车,然后拿来用。
    2)简单工厂模式:后来出现工业革命。用户不用去创建宝马车。因为客户有一个工厂来帮他创建宝马.想要什么车,这个工厂就可以建。比如想要320i系列车。工厂就创建这个系列的车。即工厂可以创建产品。
    3)工厂方法模式时代:为了满足客户,宝马车系列越来越多,如320i,523i,30li等系列一个工厂无法创建所有的宝马系列。于是由单独分出来多个具体的工厂。每个具体工厂创建一种系列。即具体工厂类只能创建一个具体产品。但是宝马工厂还是个抽象。你需要指定某个具体的工厂才能生产车出来。
   4)抽象工厂模式时代:随着客户的要求越来越高,宝马车必须配置空调。于是这个工厂开始生产宝马车和需要的空调。
   最终是客户只要对宝马的销售员说:我要523i空调车,销售员就直接给他523i空调车了。而不用自己去创建523i空调车宝马车.

 

   这就是工厂模式。


二、分类 
        工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。 
工厂模式可以分为三类: 

1)简单工厂模式(Simple Factory) 
2)工厂方法模式(Factory Method) 
3)抽象工厂模式(Abstract Factory) 

 这三种模式从上到下逐步抽象,并且更具一般性。 
        GOF在《设计模式》一书中将工厂模式分为两类:工厂方法模式(Factory Method)与抽象工厂模式(Abstract Factory)。

 

        将简单工厂模式(Simple Factory)看为工厂方法模式的一种特例,两者归为一类。

 

可以根据这篇博客进行理解:https://www.cnblogs.com/zailushang1996/p/8601808.html

 

 

观察者模式 

对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。Android中的各种Listener就使用到了这一设计模式,只要用户对手机进行操作,对应的listener就会被通知,并作出响应的处理。 
观察者模式UML图 
看不懂图的人端着小板凳到这里来,给你举个栗子��:假设有三个人,小美(女,28),老王和老李。小美很漂亮,很风骚,老王和老李是两个中年男屌丝,时刻关注着小美的一举一动。有一天,小美说了一句:我老公今天不在家,一个人好无聊啊~~~,这句话被老王和老李听到了,结果乐坏了,蹭蹭蹭,没一会儿,老王就冲到小美家门口了,于是进门了……………………..帕~啪啪啪啪啪~ 
在这里,小美是被观察者,老王和老李是观察者,被观察者发出一条信息,然后被观察者进行相应的处理,看代码:

public interface Person {
    //老王和老李通过这个接口可以接收到小美发过来的消息
    void getMessage(String s);
}

这个接口相当于老王和老李的电话号码,小美发送通知的时候就会拨打getMessage这个电话,拨打电话就是调用接口,看不懂没关系,先往下看

public class LaoWang implements Person {

    private String name = "老王";

    public LaoWang() {
    }

    @Override
    public void getMessage(String s) {
        System.out.println(name + "接到了小美打过来的电话,电话内容是:" + s);
    }

}

public class LaoLi implements Person {

    private String name = "老李";

    public LaoLi() {
    }

    @Override
    public void getMessage(String s) {
        System.out.println(name + "接到了小美打过来的电话,电话内容是:->" + s);
    }

}

代码很简单,我们再看看小美的代码:

public class XiaoMei {
    List<Person> list = new ArrayList<Person>();
     public XiaoMei(){
     }

     public void addPerson(Person person){
         list.add(person);
     }

     //遍历list,把自己的通知发送给所有暗恋自己的人
     public void notifyPerson() {
         for(Person person:list){
             person.getMessage("今天家里就我一个人,你们过来吧,谁先过来谁就能得到我!");
         }
     }
}

我们写一个测试类来看一下结果对不对

public class Test {
    public static void main(String[] args) {

        XiaoMei xiao_mei = new XiaoMei();
        LaoWang lao_wang = new LaoWang();
        LaoLi lao_li = new LaoLi();

        //老王和老李在小美那里都注册了一下
        xiao_mei.addPerson(lao_wang);
        xiao_mei.addPerson(lao_li);

        //小美向老王和老李发送通知
        xiao_mei.notifyPerson();
    }
}

运行结果我截图了 
运行结果 
完美~~~

 

装饰者模式 

对已有的业务逻辑进一步的封装,使其增加额外的功能,如java中的IO流就使用了装饰者模式,用户在使用的时候,可以任意组装,达到自己想要的效果。 
举个栗子,我想吃三明治,首先我需要一根大大的香肠,我喜欢吃奶油,在香肠上面加一点奶油,再放一点蔬菜,最后再用两片面包加一下,很丰盛的一顿午饭,营养又健康,那我们应该怎么来写代码呢? 
首先,我们需要写一个Food类,让其他所有食物都来继承这个类,看代码:

public class Food {

    private String food_name;

    public Food() {
    }

    public Food(String food_name) {
        this.food_name = food_name;
    }

    public String make() {
        return food_name;
    };
}

代码很简单,我就不解释了,然后我们写几个子类继承它:

//面包类
public class Bread extends Food {

    private Food basic_food;

    public Bread(Food basic_food) {
        this.basic_food = basic_food;
    }

    public String make() {
        return basic_food.make()+"+面包";
    }
}

//奶油类
public class Cream extends Food {

    private Food basic_food;

    public Cream(Food basic_food) {
        this.basic_food = basic_food;
    }

    public String make() {
        return basic_food.make()+"+奶油";
    }
}

//蔬菜类
public class Vegetable extends Food {

    private Food basic_food;

    public Vegetable(Food basic_food) {
        this.basic_food = basic_food;
    }

    public String make() {
        return basic_food.make()+"+蔬菜";
    }

}

这几个类都是差不多的,构造方法传入一个Food类型的参数,然后在make方法中加入一些自己的逻辑,如果你还是看不懂为什么这么写,不急,你看看我的Test类是怎么写的,一看你就明白了

public class Test {
    public static void main(String[] args) {
        Food food = new Bread(new Vegetable(new Cream(new Food("香肠"))));
        System.out.println(food.make());
    }
}

看到没有,一层一层封装,我没从里往外看:最里面我new了一个香肠,在香肠的外面我包裹了一层奶油,在奶油的外面我又加了一层蔬菜,最外面我放的是面包,是不是很形象,哈哈�� 这个设计模式简直跟现实生活中一摸一样,看懂了吗? 
我们看看运行结果吧 
运行结果 
一个三明治就做好了~~~

 

适配器模式 

将两种完全不同的事物联系到一起,就像现实生活中的变压器。假设一个手机充电器需要的电压是20V,但是正常的电压是220V,这时候就需要一个变压器,将220V的电压转换成20V的电压,这样,变压器就将20V的电压和手机联系起来了。

public class Test {
    public static void main(String[] args) {
        Phone phone = new Phone();
        VoltageAdapter adapter = new VoltageAdapter();
        phone.setAdapter(adapter);
        phone.charge();
    }
}

// 手机类
class Phone {

    public static final int V = 220;// 正常电压220v,是一个常量

    private VoltageAdapter adapter;

    // 充电
    public void charge() {
        adapter.changeVoltage();
    }

    public void setAdapter(VoltageAdapter adapter) {
        this.adapter = adapter;
    }
}

// 变压器
class VoltageAdapter {
    // 改变电压的功能
    public void changeVoltage() {
        System.out.println("正在充电...");
        System.out.println("原始电压:" + Phone.V + "V");
        System.out.println("经过变压器转换之后的电压:" + (Phone.V - 200) + "V");
    }
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值