Java设计模式个人总结(一)

本文是通过阅读《Head First 设计模式》和相关博客(http://blog.csdn.net/zhangerqing)加上自己体会写成。

一、设计模式简介

设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理地运用设计模式可以完美地解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式都描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是设计模式能被广泛应用的原因。

二、OO设计六大原则

1、开闭原则(Open Close Principle)

开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

2、里氏代换原则(Liskov Substitution Principle)

里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科

3、依赖倒转原则(Dependence Inversion Principle)

这个是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。

4、接口隔离原则(Interface Segregation Principle)

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。

5、迪米特法则(最少知道原则)(Demeter Principle)

为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

6、合成复用原则(Composite Reuse Principle)

原则是尽量使用合成/聚合的方式,而不是使用继承。有助于你保持每个类的封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。因为继承的层次增多,造成最直接的后果就是当你调用这个类群中的一个类时,就必须要把他们全部加载到栈中!(后果可想而知。)

三、设计模式分类

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

创建型模式:工厂方法模式、抽象工厂模式、单例模式。

结构型模式:适配器模式、装饰器模式、代理模式、外观模式、组合模式。

行为型模式:策略模式、模板方法模式、观察者模式、命令模式、状态模式、迭代器模式。

四、设计模式分析

1.工厂方法模式

工厂方法模式就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。

举例如下:(举一个披萨商店的例子)

首先,创建一个抽象的披萨商店的类或借口:

public interface Pizza{

  public void createPizza();

}

其次,创建具体的不同口味披萨实现类:

public class SweetPizza implements Pizza{

  public void createPizza(){

  ...       //具体实现  

}
} 


public class  SaltyPizza implements Pizza{

  public void createPizza(){

  ...       //具体实现  

}
}

最后建立工厂:

public class PizzaFactory{

public Pizza produce(String type) {
if ("sweet".equals(type)) {
return new SweetPizza();
} else if ("salty".equals(type)) {
return new SaltyPizza();
} else {
System.out.println("请输入正确的类型!");
return null;
}
}
}

测试类:

public class FactoryTest {

public static void main(String[] args) {
PizzaFactory factory = new PizzaFactory();
Pizza pizza = factory.produce("sweet");
Pizza pizza = factory.produce("salty");
}
}

2、抽象工厂模式

工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,有可能对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建一个抽象工厂接口,一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。

举例如下:

首先,创建一个Pizza接口和PizzaFactory接口:

public interface Pizza{

  public void createPizza();
}


public interface PizzaFactory{

  public void produce();
}

两个Pizza类:

public class SweetPizza implements Pizza{

  public void createPizza(){

  ...       //具体实现  

}
} 


public class  SaltyPizza implements Pizza{

  public void createPizza(){

  ...       //具体实现  

}
}

两个工厂类:

public class ChicagoPizzaFactory{

public Pizza produce(String type) {
...           //具体实现
}
}

public class BostonPizzaFactory{

public Pizza produce(String type) {
...           //具体实现
}
}

测试类:

public class FactoryTest {

public static void main(String[] args) {

                PizzaFactory chicagoFactory = new ChicagoPizzaFactory();
PizzaFactory bostonFactory = new BostonPizzaFactory();
Pizza pizza = chicagofactory.produce("sweet");
Pizza pizza = bostonfactory.produce("sweet");
}

3、单例模式

单例模式确保一个类只有一个实例,并提供全局访问点。这样的模式有几个好处:

1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。

2、省去了new操作符,降低了系统内存的使用频率,减轻GC(垃圾收集器)压力。

3、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(比如一个军队出现了多个司令员同时指挥,肯定会乱成一团),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。
首先我们写一个简单的单例类:

public class Singleton {

/* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
private static Singleton instance = null;

/* 私有构造方法,防止被实例化 */
private Singleton() {
}

/* 静态工程方法,创建实例 */
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}

/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
public Object readResolve() {
return instance;
}
}

这个类可以满足基本要求,但是,像这样毫无线程安全保护的类,如果我们把它放入多线程的环境下,肯定就会出现问题了,如何解决?我们首先会想到对getInstance方法加synchronized关键字,如下:

public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}

但是,你只有第一次执行方法时需要同步。换句话说,一旦设置好instance变量,就不再需要同步了。之后每次调用这个方法,同步都是一种累赘。所以,这个地方需要改进。我们改成下面这个:

public static Singleton getInstance() {
if (instance == null) {
synchronized (instance) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}

似乎解决了之前提到的问题,将synchronized关键字加在了内部,也就是说当调用的时候是不需要加锁的,只有在instance为null,并创建对象的时候才需要加锁,性能有一定的提升。但是,这样的情况,还是有可能有问题的。synchronized块不同于真正意义的原子性操作,执行时是可以中断的,通过锁实现了线程访问共享数据的串行化。DCL单例模式在多线程的环境下是不安全的,若线程t1恰好在instance引用获得内存地址,但是对象尚未构造完成时被中断,线程t2发现instance引用已经!=null,直接调用instance的方法和属性,将导致意想不到的错误(对象锁仍然属于线程t1)。(详情见:http://m.blog.csdn.net/niexianglin_csdn/article/details/47361003)
我们可以利用JVM的机制来优化代码:

private  class SingletonFactory{         
        private static Singleton instance = new Singleton();         
    }         
    public static Singleton getInstance(){         
        return SingletonFactory.instance;         
    } 

我们依赖JVM在加载这个类时马上创建此唯一的单件实例。JVM保证在任何线程访问instance静态变量之前,一定先创建此实例。这样我们暂时总结一个完美的单例模式:

public class Singleton {

/* 私有构造方法,防止被实例化 */
private Singleton() {
}

/* 此处使用一个内部类来维护单例 */
private static class SingletonFactory {
private static Singleton instance = new Singleton();
}

/* 获取实例 */
public static Singleton getInstance() {
return SingletonFactory.instance;
}

/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
public Object readResolve() {
return getInstance();
}
}

你可能感兴趣的:(Java设计模式)