Friday, December 11, 2015

Design Pattern



Hi ——设计原则1
         找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。换句话说,如果每次新的需求一来,都会使某方面的代码发生变化,那么你就可以确定,这部分的代码需要被抽出来,和其他稳定的代码有所区分。

——设计原则2
         针对接口编程,而不是针对实现编程。
         所谓“针对接口编程”的真正含义是“针对超类型编程”。更明确点说:变量的声明类型应该是超类型,通常是一个抽象类或者是一个接口,如此,只要是具体实现此超类型的类锁产生的对象,都可以指定给这个变量。这也意味这,声明类时不用理会以后执行时的真正对象类型。
举个栗子:
         假设有个抽象类Animal,有两个具体的实现(DogCat)继承Animal
“针对实现编程”
Dog d = new Dog();
d.bark();

而“针对接口/超类型编程”的做法会如下:
Animal animal = new Dog();
Animal.makeSound();

更棒的是,子类实例化的动作不再需要在代码中硬编程,而是在运行时才指定具体实现的对象。
a = getAnimal();
a.       makeSound();

——设计原则3
         多用组合,少用继承。

设计模式1
         策略模式   定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。


——设计原则4
         为了交互对象之间的松耦合设计而努力。
         松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降到了最低。

设计模式2
观察者模式:
JAVA 内置的观察者模式如何运作)
1.       如何把对象变成观察者?
a)         实现观察者接口(java.util.Observer),然后调用任何Observable对象的addObserver()方法。不想再当观察者时,调用deleteObserver()方法就可以了。
2.       观察者如何送出通知?
首先,你需要利用扩展java.util.Observable 接口产生“可观察者”类,然后,需要两个步骤:
a)         先调用setChanged()方法,标记状态已经改变的事实。
b)         然后调用两种notifyObservers()方法中的一个:notifyObservers()notifyObservers(Object arg)

定义:
    在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会受到通知,并自动更新

本节要点:
1.       观察者模式定义了对象之间一对多的关系。
2.       主题(也就是可观察者)用一个共同的接口来更新观察者。
3.       观察者和可观察者之间用松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口。
4.       使用此模式时,你可以被观察者处推或拉数据,(然后,推的方式被认为更“正确”)。
5.       有多个观察者时,不应该依赖特定的通知次序。
6.       Java有多种观察者模式的实现,包括了通用的java.util.Observable
7.       注意java.util.Observable实现上带来的一些问题(是类而非接口)。
8.       如果有必要的话,可以实现自己的Observable,这并不难,不要害怕。


设计模式3
装饰者模式:
伍——设计原则5
         类应该对扩展开放,对修改关闭。

定义:
装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

本节要点:
1.       继承属于扩展形式之一,但不见得是达到弹性设计的最佳方式。
2.       在我们的设计中,应该允许行为可以被扩展,而无需修改现有的代码。
3.       组合和委托可用于在运行时动态地加上新的行为。
4.       除了继承,装饰者模式也可以让我们扩展行为。
5.       装饰者模式以为这一群装饰者类,这些类用来包装具体组件。
6.       装饰者类反映出被装饰的组件类型(事实上,他们具有相同的类型,都经过接口或继承实现)。
7.       装饰者可以在被装饰者的行为前面与/或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的。
8.       你可以用无数个装饰者包装一个组件。
9.       装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。
10.   装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂。


设计模式4
工厂方法模式
        
定义:
    它定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
1.       当只有一个ConcreteCreator的时候,工厂方法模式有什么优点?
尽管只有一个具体创建者,工厂方法模式依然很有用,因为它帮助我们将产品的“实现”从“使用”中 解耦。如果增加产品或者改变产品的实现,Creator并不会受到影响(因为Creator与任何ConcreteProduct之间都不是紧耦合)。

陸——設計原則6
         依赖倒置原则(Dependency Inversion Principle)【这里的“倒置”指的是和一般OO设计的思考方式完全相反】
         要依赖抽象,不要依赖具体。

这个原则主要强调的是,不能让高层组件依赖低层组件,而且,不管高层或低层组件,“两者”都应该依赖于抽象。举个栗子,PizzaStore是个高层组件,它的行为是由比萨定义的:PizzaStore创建所有不同的比萨对象,准备、烘烤、切片、装盒;而比萨本身属于低层组件。

下面指导方针用于帮你避免在OO设计中违反依赖倒置原则:
1.       变量不可以持有具体类的引用。(如果使用new,就会持有具体类的引用。你可以改用工厂来避开这样的做法。)
2.       不要让类派生自具体类。
3.       不要覆盖基类中已实现的方法。(如果覆盖基类中已实现的方法,那么你的基类就不是一个真正适合被继承的抽象。基类中已实现的方法,应该由所有的子类共享。)



设计模式5
抽象工厂模式
定义:
    提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
它允许客户使用抽象的接口来创建一组相关的产品,而不需要关心实际产出的具体产品是什么。这样一来,客户就从具体的产品中被解耦。

本节要点:
1.       所有的工厂都是用来封装对象的创建。
2.       简单工厂,虽然不是真正的设计模式,但仍不失为一个简单的方法,可以将客户程序从具体类解耦。
3.       工厂方法使用继承:把对象的创建委托给子类,子类实现工厂方法来创建对象。
4.       抽象工厂使用对象组合:对象的创建被实现在工厂接口所暴露出来的方法中。
5.       所有工厂模式都通过减少应用程序和具体类之间的依赖促进松耦合。
6.       工厂方法允许类将实例化延迟到子类进行。
7.       抽象工厂创建相关的对象家族,而不需要依赖他们的具体类。
8.       依赖倒置原则,知道我们避免依赖具体类型,而要尽量依赖抽象。
9.       工厂是很有威力的技巧,帮助我们针对抽象变成,而不是要针对具象类编程。


设计模式6
单件模式

定义:
    确保一个类只有一个实例,并提供一个全局访问点。

         有啥用呢?
         有一些对象其实我们只需要一个,比方说:线程池(threadpool)、缓存(cache)、对话框、处理偏好设置和注册表(registry)的对象、日志对象、充当打印机、显卡等设备的驱动程序的对象。事实上,这类对象只能有一个实例,如果制造出多个实例,就会导致许多问题产生,例如:程序的行为异常、资源使用过量、或者是不一致的结果。

public class Singleton {
    private static Singleton uniqueInstance;
    private Singleton() {}

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

         getInstance()变成同步(synchronized)方法,就可以轻易地解决多线程灾难,但是你必须知道,同步一个方法可能造成程序执行效率下降100倍。因此,如果将getInstance()的程序使用在频繁运行的地方,你可能就得重新考虑了。

         怎么改善多线程呢?
1.       如果getInstance()的性能对应用程序不是很关键,就什么都别做。
2.       使用“急切”创建实例,而不用延迟实例化的做法
public class Singleton2 {
    private static Singleton2 uniqueInstance = new Singleton2();
    private Singleton2() {}
    public static synchronized Singleton2 getInstance() {
        return uniqueInstance;
    }
}
         利用这个做法,我们依赖JVM在加载这个类时马上创建此唯一的单件实例
3.       用“双重检查加锁”,在getInstance()中减少使用同步。
首先检查实例,如果尚未创建,才进入同步。这样一来,只有第一次会同步。
Volatile 关键字确保:当uniqueInstance变量被初始化成Singleton实例时,多个线程正确地处理uniqueInstance变量
public class Singleton3 {
    private volatile static Singleton3 uniqueInstance;
    private Singleton3() {}
    public static synchronized Singleton3 getInstance() {
        if (uniqueInstance == null) {
            synchronized (Singleton3.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton3();
                }
            }
        }
        return uniqueInstance;
    }
}
需要注意的是:双重检查加锁不适用于1.4及更早版本的JAVA

那么我们就结合实际的情况来分析一下二者的区别,为了说明方便,以上两种情况分别简称为 “双重检查锁”和“单重检查锁”。
 1、第一次访问Instance,同时来了10个线程。对于双重检查锁,instancenull10个线程在这里lock处排队;对于单重检查锁,10个线程在lock处排队。二者是相同的。
2、第二次、第三次。。。访问Instance,同时来了10个线程。对于双重检查锁,instance不为null10个线程不用排队,直接返回instance;对于单重检查锁,10个线程,还必须要在lock处排队。
双重检查锁的优点体现出来了:避免了不必要的排队现象。也就是说,双重检查锁的第一重检查,是很必要的,它来保证不必要的排队。
举个例子说明,病人到医院看病,第一次去的时候,都要排队去办病历本。以后再去的时候,如果有病历本,就不用再排队去办了。


使用类加载器(classloader)需要注意
         每个类加载器都定义了一个命名空间,如果有两个以上的类加载器,不同的类加载器都定义了一个命名空间,如果有两个以上的类加载器,不同的类加载器可能会加载同一个类,从整个程序来看,同一个类会被加载多次。如果这样的事情发生在单件上,就会产生多个单件并存的怪异现象。有一个解决办法:自行指定类加载器,并指定同一个类加载器。

对于全局变量和单件模式:
         首先单例模式的目的是:确保类只有一个实例并提供全局访问。在Java中,全局变量基本上就是对对象的静态引用。全局变量可以提供全局访问,但是不能确保只有一个实例。全局变量也会变相鼓励开发人员,用许多全局变量指向许多小对象来造成命名空间的污染。单件不鼓励这样的现象,当仍然可能被滥用。

所谓命名空间的污染,指的是程序中的变量和库中的变量同名了。



设计模式7
命令模式
    将“请求”封装成对象,以便使用 不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。


No comments:

Post a Comment