常用设计模式

 

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

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

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

行为型模式,共十一种:

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

一、单例模式

      只能有一个实例

      单例模式根据实例化对象时机的不同分为两种:饿汉式,懒汉式

      在单例类被加载时候,就实例化一个对象交给自己的引用

     饿汉式单例

 

public class Singleton {
   private static Singleton singleton = new Singleton();
   private Singleton(){}
   public static Singleton getInstance(){
      return singleton;
   }
}

    懒汉式单例

 

public class Singleton {
   private static Singleton singleton;
   private Singleton(){}
   public static synchronized Singleton getInstance(){
      if(singleton==null){
         singleton = new Singleton();
      }
      return singleton;
   }
}

单例模式还有一种比较常见的形式:双重锁的形式,提高了执行的效率,不必每次获取对象时都进行同步

public class Singleton{
   private static volatile Singleton instance=null;
   private Singleton(){
      //do something
   }
   public static  Singleton getInstance(){
      if(instance==null){
         synchronized(SingletonClass.class){
            if(instance==null){
               instance=new Singleton();
            }
         }
      }
      return instance;
   }
}

单例模式的优点:

    1,在内存中只有一个对象,节省内存空间。

    2,避免频繁的创建销毁对象,可以提高性能。

    3,避免对共享资源的多重占用。

    4,可以全局访问。

单例模式的优点:

    1,扩展困难,由于getInstance静态函数没有办法生成子类的实例。如果要拓展,只有重写那个类。

    2,隐式使用引起类结构不清晰。

    3,导致程序内存泄露的问题

注意:

1)在jdk中的应用:Runtime.getRuntime(), Calendar.getInstance()和其他的一些类中。

2)在jvm中,因该类的实例都已经被不能回收,所以不会被jvm垃圾收集器收集。

3)在分布式系统、多个类加载器、以及序列化的的情况下,会产生多个单例,使用反射方式,也将会得到新的单例

4)单例类不可以被继承

二、工厂模式

 为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来

 根据抽象程度的不同分为三种:

1)简单工厂模式:由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类

2)工厂方法模式:由子类决定实例化具体的某一个类

3)抽象工厂模式:围绕一个超级工厂创建其他工厂。由一个工厂生成器类生成对应的子工厂,再由子工厂生成对应的子类

 

工厂模式案例:

public interface ICarFactory {
   Car getCar();
}
public class BikeFactory implements ICarFactory {
   @Override
   public Car getCar() {
      return new Bike();
   }
}

public class BusFactory implements ICarFactory {
   @Override
   public Car getCar() {
      return new Bus();
   }
}

public class TestFactory {
   @Test
   public void test() {
      ICarFactory factory = null;
      // bike
      factory = new BikeFactory();
      Car bike = factory.getCar();
      bike.gotowork();

      // bus
      factory = new BusFactory();
      Car bus = factory.getCar();
      bus.gotowork();
   }
}

优点:1)降低了耦合度    2)扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以  3)调用者只关心产品的接口

缺点:每次想增加一个产品时,都需要增加一个具体类和对象实现工厂,增加了复杂度

应用:jdk中的Boolean.valueOf()

三、原型模式

通过复制现有的对象实例来创建新的对象实例。可以通过实现Cloneable接口,重写Object类中的clone方法(作用域protected类型的,一般的类无法调用,需要将clone方法的作用域修改为public类型)

 

优点:使用原型模型创建一个对象比直接new一个对象更有效率,因为它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。

 

缺点:1,由于使用原型模式复制对象时不会调用类的构造方法,所以原型模式无法和单例模式组合使用,因为原型类需要将clone方法的作用域修改为public类型,那么单例模式的条件就无法满足了。

        2,使用原型模式时不能有final对象。

        3,Object类的clone方法只会拷贝对象中的基本数据类型,对于数组,引用对象等只能另行拷贝

 

四、适配器模式

 

概念:将一个类的接口转换成客户希望的另外一个接口。使得原本由于接口不兼容而不能一起工作的那些类可以一起工作

分类:类适配器模式、对象适配器模式

区别:对象的适配器模式不是使用继承关系连接到Adaptee类,而是使用委派关系连接到Adaptee

1)类适配器模式

 

class Adaptee {
   publicvoid specificRequest() {
      System.out.println("特殊请求,这个是源角色");
   }
}

interface Target {
   publicvoid request();
}

class Adapter extends Adaptee implements Target{
   publicvoid request() {
      super.specificRequest();
   }
}

2)对象适配器模式

 

class Adapter implements Target{
   privateAdaptee adaptee;

   publicAdapter (Adaptee adaptee) {
      this.adaptee= adaptee;
   }

   publicvoid request() {
      this.adaptee.specificRequest();
   }
}

优点:更好的复用以及扩展性  

缺点:过多的使用适配器,会让系统非常零乱

五、装饰者模式

在不必改变原类文件和原类使用的继承的情况下,动态地扩展一个对象的功能

 

public interface Person {
   void eat();
}

public class OldPerson implements Person {
   @Override
   public void eat() {
      System.out.println("吃饭");
   }
}

public class NewPerson implements Person {
   private OldPerson p;

   NewPerson(OldPerson p) {
      this.p = p;
   }

   @Override
   public void eat() {
      System.out.println("生火");
      System.out.println("做饭");
      p.eat();
      System.out.println("刷碗");
   }
}

public class PersonDemo {
   public static void main(String[] args) {
      OldPerson old = new OldPerson();
      //old.eat();
      NewPerson np = new NewPerson(old);
      np.eat();
   }
}

//没有改变原来的OldPerson类,同时也没有定义他的子类而实现了Person的扩展

优点:通过一种动态的方式来扩展一个对象的功能

缺点:会产生很多的小对象,增加了系统的复杂性

应用:装饰器模式被用于多个java IO类

 

装饰者与适配者模式的区别:

   1,适配器模式主要用来兼容那些不能在一起工作的类,使他们转化为可以兼容目标接口,虽然也可以实现和装饰者一样的增加新职责,但目的不在此。装饰者模式主要是给被装饰者增加新职责的。

   2,适配器模式是用新接口来调用原接口,原接口对新系统是不可见或者说不可用的。

        装饰者模式原封不动的使用原接口,系统对装饰的对象也通过原接口来完成使用。

   3,适配器是知道被适配者的详细情况的(就是那个类或那个接口)。

        装饰者只知道其接口是什么,至于其具体类型(是基类还是其他派生类)只有在运行期间才知道

六、代理模式

 为其他对象提供一种代理以控制对这个对象的访问。一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用

分类:静态代理、动态代理

1)静态代理:静态代理也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了

2)动态代理:程序在运行期间由JVM根据反射等机制动态生成的,所以不存在代理类的字节码文件。代理角色和真实角色的联系在程序运行时确定

静态代理:

 

//抽象角色,真实对象和代理对象共同的接口
public interface UserInfo{
   public void queryUser ();
}

//真实角色
public class UserImpl implementsUserInfo{
   @Override
   public void queryUser() {
      //查询方法略...
   }
}

//代理角色
public class UserProxy implementsUserInfo{
   private UserInfo userImpl;
   public AccountProxy(UserInfo userImpl) {
      this.userImpl = userImpl;
   }

   @Override
   public void queryUser() {
      //这里可以扩展,增加一些查询之前需要执行的方法
      //查询方法略...
      //这里可以扩展,增加一些查询之后需要执行的方法
   }
}

public class Test {
   public static void main(String[] args) {
      UserInfo userImpl = new UserImpl();
      UserInfo userProxy = new UserProxy(userImpl);
      userProxy.queryUser();
   }
}

动态代理:

 

public interface UserInfo{
   public void queryUser ();
}

public class UserImpl implementsUserInfo{
   @Override
   public void queryUser() {
      //查询方法略...
   }
}

public class UserHandler implements InvocationHandler{

   private UserInfo userImpl;
   public UserHandler(UserInfo userImpl2){
      this.userImpl= userImpl2;
   }
   @Override
   public Object invoke(Object proxy, Method method, Object[] args)
         throws Throwable {
      Objectobject = null;
      //方法开始前做一些事情
      if (method.getName().equals("queryUser")) {
         object = method.invoke(userImpl, args);
         //激活调用的方法
      }
      //方法结束后做一些事情
      return object;
   }
}

public class Test {
   public static void main(String[] args) {
      UserInfouserImpl =new UserImpl();
      UserHandlerhandler = new UserHandler(userImpl);
      UserInfouserProxy = (UserInfo)Proxy.newProxyInstance
            (ClassLoader.getSystemClassLoader(),
                  newClass[]{UserInfo.class}, handler);
      userProxy.queryUser();
   }
}

优点:1)业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点 2)降低了系统的耦合度

缺点:由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢,例如保护代理

七、策略模式

定义个策略接口,不同的实现类提供不同的具体策略算法, 同时它们之间可以互相替换

与模板方法模式区别仅仅是多了一个单独的封装类

 

interface IStrategy {
   public void doSomething();
}
class ConcreteStrategy1 implements IStrategy {
   public void doSomething() {
      System.out.println("具体策略1");
   }
}
class ConcreteStrategy2 implements IStrategy {
   public void doSomething() {
      System.out.println("具体策略2");
   }
}
class Context {
   private IStrategy strategy;

   public Context(IStrategy strategy){
      this.strategy = strategy;
   }

   public void execute(){
      strategy.doSomething();
   }
}

public class Client {
   public static void main(String[] args){
      Context context;
      System.out.println("-----执行策略1-----");
      context = new Context(new ConcreteStrategy1());
      context.execute();

      System.out.println("-----执行策略2-----");
      context = new Context(new ConcreteStrategy2());
      context.execute();
   }
}

优点:1)策略类之间可以自由切换,由于策略类实现自同一个抽象,所以他们之间可以自由切换

          2)易于扩展,增加一个新的策略对策略模式来说非常容易,基本上可以在不改变原有代码的基础上进行扩展

缺点:1)必须对客户端(调用者)暴露所有的策略类,因为使用哪种策略是由客户端来决定的,因此,客户端应该知道有什么             策略,并且了解各种策略之间的区别。

          2)维护各个策略类会给开发带来额外开销

八、模板方法模式

 定义一个操作的算法骨架, 并将一些步骤延迟到子类中

 

abstract class AbstractSort {
   protected abstract void sort(int[] array);
   public void showSortResult(int[] array){
      this.sort(array);
      System.out.print("排序结果:");
      for (int i = 0; i < array.length; i++){
         System.out.printf("%3s", array[i]);
      }
   }
}

class ConcreteSort extends AbstractSort {
   @Override
   protected void sort(int[] array){
      for(int i=0; i<array.length-1; i++){
         selectSort(array, i);
      }
   }
   private void selectSort(int[] array, int index) {
      int MinValue = 32767; // 最小值变量
      int indexMin = 0; // 最小值索引变量
      int Temp; // 暂存变量
      for (int i = index; i < array.length; i++) {
         if (array[i] < MinValue){ // 找到最小值
            MinValue = array[i]; // 储存最小值
            indexMin = i;
         }
      }
      Temp = array[index]; // 交换两数值
      array[index] = array[indexMin];
      array[indexMin] = Temp;
   }
}

public class Client {
   public static int[] a = { 10, 32, 1, 9, 5, 7, 12, 0, 4, 3 };
   // 预设数据数组  
   public static void main(String[] args){
      AbstractSort s = new ConcreteSort();
      s.showSortResult(a);
   }
}

优点:1)容易扩展 2)便于维护

九、观察者模式

定义对象间一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新

//被观察者角色
public abstract class Subject {
   private Vector<Observer> obs = new Vector<Observer>();  //vector 线程安全  list 线程不安全

   public void addObserver(Observer obs){
      this.obs.add(obs);
   }
   public void delObserver(Observer obs){
      this.obs.remove(obs);
   }
   protected void notifyObserver(){
      for(Observer o: obs){
         o.update();
      }
   }
   public abstract void doSomething();
}
//具体被观察者校色
public class ConcreteSubject extends Subject {
   public void doSomething(){
      System.out.println("被观察者事件反生");
      this.notifyObserver();
   }
}
//观察者角色一般是一个接口
public interface Observer {
   public void update();
}
//具体观察者角色
public class ConcreteObserver1 implements Observer {
   public void update() {
      System.out.println("观察者1收到信息,并进行处理。");
   }
}
public class ConcreteObserver2 implements Observer {
   public void update() {
      System.out.println("观察者2收到信息,并进行处理。");
   }
}

public class Client {
   public static void main(String[] args){
      Subject sub = new ConcreteSubject();
      sub.addObserver(new ConcreteObserver1()); //添加观察者1
      sub.addObserver(new ConcreteObserver2()); //添加观察者2
      sub.doSomething();
   }
}

优点:观察者与被观察者之间是属于轻度的关联关系,并且是抽象耦合的,这样,对于两者来说都比较容易进行扩展

缺点:1)由于是链式触发,当观察者比较多的时候,性能问题是比较令人担忧的。

         2)在链式结构中,比较容易出现循环引用的错误,造成系统假死

十、桥接模式

将抽象部分与实现部分分离,使它们都可以独立的变化

 

//Implementor : 定义实现接口。
interface Implementor {
   // 实现抽象部分需要的某些具体功能
   public void operationImpl();
}

class ConcreteImplementorA implements Implementor {
   @Override
   public void operationImpl() {
      // 真正的实现
      System.out.println("具体实现A");
   }
}

//Abstraction : 定义抽象接口。
abstract class Abstraction {
   // 持有一个 Implementor 对象,形成聚合关系
   protected Implementor implementor;
   public Abstraction(Implementor implementor) {
      this.implementor = implementor;
   }
   // 可能需要转调实现部分的具体实现
   public void operation() {
      implementor.operationImpl();
   }
}

class RefinedAbstraction extends Abstraction {
   public RefinedAbstraction(Implementor implementor) {
      super(implementor);
   }
   public void otherOperation() {
      // 实现一定的功能,可能会使用具体实现部分的实现方法,
      // 但是本方法更大的可能是使用 Abstraction 中定义的方法,
      // 通过组合使用 Abstraction 中定义的方法来完成更多的功能。
   }
}
public class BridgePattern {
   public static void main(String[] args) {
      Implementor implementor = new ConcreteImplementorA();
      RefinedAbstraction abstraction = new RefinedAbstraction(implementor);
      abstraction.operation();
      abstraction.otherOperation();
   }
}

十一、外观模式

为系统向外界提供一个统一的接口

public class SubClass1 {
   public void method1(){
      System.out.println("这是子系统类1中的方法1");
   }
   public void method2(){
      System.out.println("这是子系统类1中的方法2");
   }
}
public class SubClass2 {
   public void method1(){
      System.out.println("这是子系统类2中的方法1");
   }
   public void method2(){
      System.out.println("这是子系统类2中的方法2");
   }
}
public class SubClass3 {
   public void method1(){
      System.out.println("这是子系统类3中的方法1");
   }
   public void method2(){
      System.out.println("这是子系统类3中的方法2");
   }
}
public class FacadeClass {
   public void FacadeMethod(){
      SubClass1 s1 = new SubClass1();
      s1.method1();
      SubClass2 s2 = new SubClass2();
      s2.method1();
      SubClass3 s3 = new SubClass3();
      s3.method1();
   }
}

public class ClientClass {
   public static void main(String[] args) {
      FacadeClass fc = new FacadeClass();
      fc.FacadeMethod();
   }
}

优点:1)实现了子系统与客户端之间的松耦合关系

          2)客户端屏蔽了子系统组件,减少了客户端所需处理的对象数目,并使得子系统使用起来更加容易

十二、命令模式

 将一个请求封装成为一个对象, 使可以用不同的请求对客户进行参数化

 

Action 封装了具体行为,Command 封装了 Action 并提供空方法 execute() ,它的子类通过重写该方法可在方法里调用 mAction 不同行为达到封装命令的目的,最后 Client 封装了一系列的 Command 对象,并可以通过 notify() 方法一个接着一个调用所持有 Command 对象们的 execute() 方法达到给 Action 传达命令的目的

 

class Invoker {
   private Command command;
   public void setCommand(Command command) {
      this.command = command;
   }
   public void action(){
      this.command.execute();
   }
}

abstract class Command {
   public abstract void execute();
}

class ConcreteCommand extends Command {
   private Receiver receiver;
   public ConcreteCommand(Receiver receiver){
      this.receiver = receiver;
   }
   public void execute() {
      this.receiver.doSomething();
   }
}

class Receiver {
   public void doSomething(){
      System.out.println("接受者-业务逻辑处理");
   }
}

public class Client {
   public static void main(String[] args){
      Receiver receiver = new Receiver();
      Command command = new ConcreteCommand(receiver);
      //客户端直接执行具体命令方式(此方式与类图相符)  
      command.execute();

      //客户端通过调用者来执行命令  
      Invoker invoker = new Invoker();
      invoker.setCommand(command);
      invoker.action();
   }
}

优点:封装性好,对命令的扩展性好

缺点:如果命令过多,导致操作繁琐,对于简单的命令,需要对他进行封装

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值