总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:
策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
一、单例模式
只能有一个实例
单例模式根据实例化对象时机的不同分为两种:饿汉式,懒汉式
在单例类被加载时候,就实例化一个对象交给自己的引用
饿汉式单例
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();
}
}
优点:封装性好,对命令的扩展性好
缺点:如果命令过多,导致操作繁琐,对于简单的命令,需要对他进行封装