zoukankan      html  css  js  c++  java
  • 设计模式

    1、什么是设计模式

    设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。

    一些简单的设计模式略过,如常量接口模式、标识类型模式。

    以下,将从最简单的设计模式开始讲起:

    2、单例模式 Singleton

        单例模式是指一个类只能拥有唯一的实例。为了保证这一点,我们必须要限制类的创建。即某个单例类,只可以实例化一次。那怎么才可以做到呢?

         实现思路如下:

    给需要单例的类声明私有的构造方法。这样就限制了其他类对此类的随意创建。

    在单例类内部,声明自己的静态成员变量,且实例化自己。由在当前类中,是可以访问自己的所有私有成员及方法的,所以,即使构造方法为私有也可以成功创建对象。

    一般情况下,再提供一个静态方法以返回创建的喻一对象。一般情况下,我们将此方法取名为newInstace,getInstance等,不过这并不是硬性的规定。

    示例代码如下:

    public class SingletonDemo { 
    
        //私有化构造方法 
    
        private SingletonDemo(){} 
    
        //声明静态私有成员变量 
    
        private static SingletonDemo demo; 
    
        //提供静态工厂方法 
    
        public static SingletonDemo getInstance(){ 
    
            if(demo==null){ 
    
                demo = new SingletonDemo(); 
    
            } 
    
            return demo; 
    
        } 
    
    }

    4、多例模式 Multition

    既然有单例,就会有多例。多例是指限定一个类的只能创建N(具体数量)个的设计模式。如以下性别类,就只能创建两个实例。实现思路是在本类中声明多个类的实例,并提供静态方法返回不同的实例即可:

    第一种实现:

    public class Gender { 
    
        private String sex; 
    
        private Gender(String sex){ 
    
            this.sex = sex; 
    
        } 
    
        //男性 
    
        private static Gender MALE; 
    
        //女性 
    
        private static Gender FEMALE; 
    
        public static Gender getMale(){ 
    
            if(MALE==null){ 
    
                MALE = new Gender("男"); 
    
            } 
    
            return MALE; 
    
        } 
    
        public static Gender getFemale(){ 
    
            if(FEMALE==null){ 
    
                FEMALE = new Gender("女"); 
    
            } 
    
            return FEMALE; 
    
        } 
    
    }
    第二种实现:   
    public class Gender { 
    
        private String sex; 
    
        private Gender(String sex){ 
    
            this.sex = sex; 
    
        } 
    
        //男性 
    
        private static Gender MALE; 
    
        //女性 
    
        private static Gender FEMALE; 
    
        static{//静态代码块中初始化 
    
            MALE = new Gender("男"); 
    
            FEMALE = new Gender("女"); 
    
        } 
    
        public static Gender getMale(){ 
    
            return MALE; 
    
        } 
    
        public static Gender getFemale(){ 
    
            return FEMALE; 
    
        } 
    
    }
    第三种实现:直接使用枚举
    public enum Gender{ 
    
        MALE("男"),FEMALE("女"); 
    
        private String sex; 
    
        private Gender(String sex){ 
    
            this.sex=sex; 
    
        } 
    
        @Override 
    
        public String toString() { 
    
            return sex; 
    
        } 
    
    }

    5、工厂方法模式Factory Method

    工厂方法指在一个类内部,提供一个静态方法,返回自己的实例或是其他类的实例。具体是返回自己的实例,还是返回其他类的实例,这个要看具体业务的需求。我们的数据连接池返回的就是其他对象的实例:

    public class FactoryDemo{ 
    
        private static Connection con; 
    
        static{ 
    
        //在此连接数据库 
    
        con = DriverManager.getConnection(…); 
    
    } 
    
    //提供一个静态工厂方法 
    
    public static Connection getConn(){ 
    
        return con; 
    
    } 
    
    }

    6、适配置器模式 Adapter

    一个接口,往往定义很多方法,但实现此接口的类,可能只需要几个方法。那么那些多余的方法,占据了我们大量的代码,将失去意义。那怎么才可以即省去一些无用的方法,又实现此接口呢?这就是适配置器模式。

    Awt开发中的Listener大量使用了此模式。如WindowListener和WindowAdapter。

    请见上面两个类(WindowListener和WindowAdapter)的源代码。

    7、装饰器设计模式(Decorator)也叫包装设计模式(Wrapper)

        如果一个类,即是某个类的子类,又拥有某个类的一个成员变量,则被叫做包装模式。 如:

    public Interface IA{} //定义一个接口 
    
    public class B implements IA{ //首先B是IA的子类 
    
        private IA ia;             //然后B拥有一个IA的成员变量。 
    
    }
    使用装饰器或是包装模式一般是为了增强类的功能。如增强某个方法等。

    8、代理模式 Proxy

    代理模式的定义:对其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

    假设有一个Italk接口,有空的方法talk()(说话),所有的people对象都实现(implements)这个接口,实现talk()方法,前端有很多地方都将people实例化,执行talk方法,后来发现这些前端里有一些除了要说话以外还要唱歌(sing),那么我们既不能在Italk接口里增加sing()方法,又不能在每个前端都增加sing方法,我们只有增加一个代理类talkProxy,这个代理类里实现talk和sing方法,然后在需要sing方法的客户端调用代理类即可,代码如下:

    //定义接口 
    
    interface ITalk { 
    
        public void talk(String msg); 
    
    } 
    
    //定义一个代理,接收ITalk,并增加一个方法sing 
    
    class TalkProxy implements ITalk{ 
    
        private ITalk talker; 
    
        private String music; 
    
        public TalkProxy(ITalk talker,String music){ 
    
            this.talker=talker; 
    
            this.music = music; 
    
        } 
    
         
    
        @Override 
    
        public void talk(String msg) { 
    
            talker.talk(msg); 
    
            sing(); 
    
        } 
    
        //再多定义一个唱歌方法 
    
        private void sing(){ 
    
            System.err.println("正在唱:"+music); 
    
        } 
    
    } 
    
    //接口的一个实现类 
    
    class Tom implements ITalk{ 
    
        public void talk(String msg) { 
    
            System.err.println("正在说话:"+msg); 
    
        } 
    
    } 
    
    //测试调用 
    
    public class ProxyDemo{ 
    
        public static void main(String[] args) { 
    
            TalkProxy proxy = new TalkProxy(new Tom(),"十里香"); 
    
            proxy.talk("Hello"); 
    
        } 
    
    }


    9、动态代理 Dync Proxy

        在JDK中,Proxy类和InvocationHandler可以在不添加任何代码的情况下增强某个方法:

    package cn.itcast.pattern; 
    
    import java.lang.reflect.InvocationHandler; 
    
    import java.lang.reflect.Method; 
    
    import java.lang.reflect.Proxy; 
    
    import java.util.ArrayList; 
    
    import java.util.List; 
    
    //测试代码 
    
    public class DyncProxyDemo { 
    
        public static void main(String[] args) { 
    
            List list =(List) MyProxy.getProxy(new ArrayList()); 
    
            list.add("Hello"); 
    
        } 
    
    } 
    
    //书写代理类 
    
    class MyProxy implements InvocationHandler{ 
    
        //声明被代理之前的对象 
    
        private Object src; 
    
        //私有构造方法 
    
        private MyProxy(Object src){ 
    
            this.src=src; 
    
        } 
    
        //提供一个静态方法以返回被代理以后的对象 
    
        public static Object getProxy(Object src){ 
    
            Object dest = null;//被代理以后的对象 
    
            try{ 
    
                //处理代理的核心代码 
    
                dest = Proxy.newProxyInstance(MyProxy.class.getClassLoader(), 
    
                            src.getClass().getInterfaces(), 
    
                            new MyProxy(src)); 
    
            }catch(Exception e){ 
    
                throw new RuntimeException(e.getMessage(),e); 
    
            } 
    
            return dest; 
    
        } 
    
        //拦截方法 
    
        public Object invoke(Object proxy, Method method, Object[] args) 
    
                throws Throwable { 
    
            System.err.println("正直执行方法:"+method.getName()); 
    
            Object oo = method.invoke(src, args); 
    
            return oo; 
    
        } 
    
         
    
    }


    10、观察者模式Observer

        观察者模式又叫做发布订阅模式。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实作事件处理系统。如Awt中的监听器Listener都是采用了种模式。

        在此种模式下,观察者只是一个接口,即定义一个规范,具体的实现应该是客户端完成。就像Swing中的注册一个监听器一样如:

    JFrame frame = new Jframe();//声明被观察,被监听者对象

    frame.addWindowListener(new WindowListener(){…});//添加一个实现WindowListener的观察者

    frame.setVisible(true);    //执行一些后续的操作,只要注册了观察者,就会被观察者所监听到

    示例代码如下:

     

    public class ObserverDemo { 
    
        public static void main(String[] args) { 
    
            //杰克逊对象 
    
            Singer jackson = new Singer(); 
    
            //注册观察者 
    
            jackson.addListener(new SingListener() { 
    
                public void listener(Singer singer) { 
    
                    System.err.println("监听到了手的正在唱歌。。。。"); 
    
                } 
    
            }); 
    
            //唱歌 
    
            jackson.sing(); 
    
        } 
    
    } 
    
    //设置一个监听器接口-监听谁在唱歌 
    
    interface SingListener{ 
    
        public void listener(Singer singer); 
    
    } 
    
    //一个可能的被监听者 
    
    class Singer{ 
    
        //声明观察者 
    
        private SingListener listener; 
    
        public void addListener(SingListener sl){ 
    
            this.listener=sl; 
    
        } 
    
        public void sing(){ 
    
            //判断是否有观察者 
    
            if(this.listener!=null){ 
    
                this.listener.listener(this); 
    
            } 
    
            System.err.println("歌手正在唱歌"); 
    
        } 
    
    }
     

    给观察者添加一个观察事件对象,SingEvent,以便于解藕:

    完整的示例代码如下:

    package cn.itcast.pattern; 
    
    //测试代码 
    
    public class ObserverDemo { 
    
        public static void main(String[] args) { 
    
            Singer jackson = new Singer(); 
    
            jackson.addListener(new SingListener() { 
    
                public void listener(SingEvent event) { 
    
                    System.err.println("歌手是:"+event.getSource()); 
    
                } 
    
            }); 
    
            jackson.sing(); 
    
        } 
    
    } 
    
    //设置一个监听器接口-监听谁在唱歌 
    
    interface SingListener{ 
    
        public void listener(SingEvent event); 
    
    } 
    
    //设计一个事件对象 
    
    class SingEvent{ 
    
        //接收事件对象,即被观察者 
    
        private Object source; 
    
        public SingEvent(Object source){ 
    
            this.source=source; 
    
        } 
    
        public Object getSource() { 
    
            return source; 
    
        } 
    
    } 
    
    //一个可能的被监听者 
    
    class Singer{ 
    
        //声明观察者 
    
        private SingListener listener; 
    
        public void addListener(SingListener sl){ 
    
            this.listener=sl; 
    
        } 
    
        public void sing(){ 
    
            //判断是否有观察者 
    
            if(this.listener!=null){ 
    
                this.listener.listener(new SingEvent(this)); 
    
            } 
    
            System.err.println("歌手正在唱歌"+this); 
    
        } 
    
    }
     

    11、策略模式Strategy

    策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。

    策略模式的组成

    -抽象策略角色: 策略类,通常由一个接口或者抽象类实现。

    -具体策略角色:包装了相关的算法和行为。

    -环境角色:持有一个策略类的引用,最终给客户端调用。

    package cn.itcast.pattern; 
    
    /** 
    
     * 策略模式 
    
    */ 
    
    public class StrategyDemo { 
    
        public static void main(String[] args) { 
    
            Context ctx = new Context(new JavaProgrammer()); 
    
            ctx.doSth(); 
    
        } 
    
    } 
    
    //设计一个公共的接口 
    
    interface IStrategy{ 
    
        public void doSth(); 
    
    } 
    
    //分别书写多个实现类,有不同的实现策略 
    
    class JavaProgrammer implements IStrategy{ 
    
        public void doSth() { 
    
            System.err.println("写Java代码"); 
    
        } 
    
    } 
    
    class PhpProgrammer implements IStrategy{ 
    
        public void doSth() { 
    
            System.err.println("写PHP代码"); 
    
        } 
    
    } 
    
    //实现策略类 
    
    class Context{ 
    
        private IStrategy strategy; 
    
        public Context(IStrategy strategy){ 
    
            this.strategy = strategy; 
    
        } 
    
        //具体调用 
    
        public void doSth(){ 
    
            this.strategy.doSth(); 
    
        } 
    
    }
     

    12、门面设计模式

    门面设计模式的结构:

    没有门面设计模式的调用方法:

    有了门面设计模式的类框架图如下:即由原来的客户端一一调用每一个系统,修改成一使用一个统一中调用:

    具体实现代码略。

     

    13、命令模式Command

    在软件系统中,"行为请求者"与"行为实现者"通常呈现一种"紧耦合"。但在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,实现二者之间的松耦合。这就是命令模式(Command Pattern)

     

    示例代码如下:

    package cn.itcast.pattern; 
    
    public class CommandDemo { 
    
        public static void main(String[] args) { 
    
            //命令的接收者只有一个 
    
            Receiver receiver = new Receiver(); 
    
            //定义命令的执行者 
    
            Invoker invoker = new Invoker(); 
    
            //通过Command类具体通知命令的接收者做什么工作 
    
            invoker.setCommand(new CommandA(receiver)); 
    
            invoker.execute(); 
    
            //再设置第二个命令 
    
            invoker.setCommand(new CommandB(receiver)); 
    
            invoker.execute(); 
    
        } 
    
    } 
    
    //定义命令 
    
    abstract class Command{ 
    
        //定义接收命令对象 
    
        protected Receiver receiver; 
    
        public Command(Receiver rec){ 
    
            this.receiver=rec; 
    
        } 
    
        public abstract void execute(); 
    
    } 
    
    //指定接收者的工作 
    
    class Receiver{ 
    
        public void actionA(){ 
    
            System.err.println("做第一件事"); 
    
        } 
    
        public void actionB(){ 
    
            System.err.println("做第二件事"); 
    
        } 
    
    } 
    
    //执行者 
    
    class Invoker{ 
    
        //接收一个命令 
    
        private Command command; 
    
        public void setCommand(Command cmd){ 
    
            this.command = cmd; 
    
        } 
    
        //执行命令 
    
        public void execute(){ 
    
            command.execute(); 
    
        } 
    
    } 
    
    //定义两个具体的命令 
    
    class CommandA extends Command{ 
    
        public CommandA(Receiver rec) { 
    
            super(rec); 
    
        } 
    
        @Override 
    
        public void execute() { 
    
            this.receiver.actionA(); 
    
        } 
    
    } 
    
    //定义两个具体的命令 
    
    class CommandB extends Command{ 
    
        public CommandB(Receiver rec) { 
    
            super(rec); 
    
        } 
    
        @Override 
    
        public void execute() { 
    
            this.receiver.actionB(); 
    
        } 
    
    }
     

    14、原型模式Prototype

    用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

    Prototype原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。

    它主要面对的问题是:"某些结构复杂的对象"的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是他们却拥有比较稳定一致的接口。

      如何使用?

      因为Java中的提供clone()方法来实现对象的克隆,所以Prototype模式实现一下子变得很简单。

    代码略。

     

    15、亨元模式flyweight

    可以理解成为轻量级模式,是一种软件设计模式。

    面向对象的思想很好地解决了抽象性的问题,一般也不会出现性能上的问题。但是在某些情况下,对象的数量可能会太多,从而导致了运行时的代价。那么我们如何去避免大量细粒度的对象,同时又不影响客户程序使用面向对象的方式进行操作?

    见下面的代码:

    //使用Integer对象的示例 
    
            Integer b1 = 100; 
    
            Integer b2 = 100; 
    
            System.err.println(b1==b2); //true 
    
            //由于大量细粒度的对象出现在128以下,所以,下现的值为false 
    
            Integer b3 = 128; 
    
            Integer b4 = 128; 
    
        System.err.println(b3==b4); //false
     

    16、总结

        在实际的开发中,单例、多例、工厂、适配置器、代理、装饰、包装是经常使用的发模式,其他模式只需要了解就可以了。

  • 相关阅读:
    并发编程
    网络与WEB 编程
    包和模块
    元编程
    类和对象
    【算法题 14 LeetCode 147 链表的插入排序】
    剑指offer面试54题
    剑指offer 面试51题
    剑指offer 面试3题
    剑指offer 面试52题
  • 原文地址:https://www.cnblogs.com/starzy/p/10376606.html
Copyright © 2011-2022 走看看