设计模式初学总结
一、设计模式:
设计模式是个好东西,它给出了很多设计中的技巧与思路,对于很多优秀的设计,它加以总结与提炼。设计模式并非四人团拍脑瓜想出来的,而是他们搜集了其他人优秀的设计,加以整理出来的,他们不是这些模式的创造者,仅仅是整理者。
应用设计模式会给我们带来很多好处:软件将变得更加灵活,模块之间的耦合度将会降低,效率会提升,开销会减少。更重要的,设计模式就好像美声唱法中的花腔,让你的设计更加漂亮。总的来说,设计模式似乎将软件设计提升到艺术的层次。
设计模式已经被广泛的应用了,在现在很多的图形界面框架都使用了MVC模式,大量跌代器模式的应用,彻底改变了我们对集合的操作方式。不仅如此,应用了设计模式的设计,往往被看成为优秀的设计。这是因为,这些设计模式都是久经考验的。
初次接触设计模式,觉得是个好东西,查资料发现目前有23中设计模式,但比较抽象,因此对目前接触到的几种模式进行了总结。
二、单例设计模式
原则:保证一个类在内存中只有一个对象
想要保证对象唯一。
1,为了避免其他程序过多建立该类对象,需要先禁止其他程序建立对象
2,为了保证其他程序可以访问该类对象,只好在本类中先定义一个对象
3,为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式
用代码体现就是:
1,将构造函数私有化
2,在类中创建一个本类对象
3,提供一个方法可以获取到该对象
//单例中的 饿汉式 class Single { private static Single s = new Single(); private Single(){} public static Single getInstance() { return s; } }
//懒汉式 也称为延迟加载 class Single { private static Single s = null; private Single(){} public static Single getInstance() { if(s==null) { synchronized(Single.class) { if(s==null) //在多线程中此处会出现安全隐患,因此需要进行加锁 s = new Single(); } } return s; } }
二、模板方法设计模式
在定义功能时,功能的一部分是确定的,但是有一部分是不确定的,而确定的部分在使用不确定的部分。那么这时就将不确定的部分暴漏出去。由该类的子类去完成。
//例子:模板方法设计模式,获得代码的运行时间 abstract class GetTime { public final void getTime() { long start = System.currentTimeMills(); runcode(); long end = System.currentTimeMills(); System.out.println("毫秒:"+(end-start)); } public abstract void runcode(); } class Test extends GetTime { public void runcode() { .... } }
三、装饰设计模式:
当想要对已有的对象进行功能增强时,可以定义一个类,将已有对象传入,基于已有的功能并进行加强。那么我们可以称这个自定义的类为装饰类。
通过一个非常简单的小例子引入:
class Person
{
void eat()
{
System.out.println("吃饭");
}
}
class PersonDemo
{
public static void main(String[] args)
{
Person p = new Person();
p.eat();
}
}
当我们需要对上面的吃饭功能进行增强时,可以进行如下方式:
class SuperPerson
{
private Person p;
SuperPerson(Person p)
{
this.p = p;
}
public void superEat()
{
System.out.println("来杯开胃酒");
p.eat();
System.out.println("来点甜点");
System.out.println("来一根");
}
}
此时对与ClassDemo而言,只需要再加两行代码即可:
SuperPerson sp = new SuperPerson(p);
sp.eat();
总结发现装饰设计模式其实就是通过构造函数传递进行功能增强。
但是用继承其实可以达到同样的目的,但相比之下我们就会发现真正构建出来的继承体系会非常臃肿。
举例子如下:
假设有一个专门用于读操作的类MyReader,然后它会有一些列的子类:
MyReader
|----MyTextReader
|----MyBufferTextReader
|----MyMediaReader
|----MyBufferMediaReader
|----MyDataReader
|----MyBufferDataReader
|
......................
随着时间的增长,我们的类会变得非常臃肿,看起来相当难受,为此可以对此进行一次装饰:
class MyBufferReader extends MyReader
{
MyBufferReader(MyReader r)
{}
}
此时我们的体系结构就可以变成下面这个样子:
MyReader
|----MyTextReader
|----MyMediaReader
|----MyDataReader
|----MyBufferReader
因此装饰模式要比继承灵活许多,由于装饰类的存在是为了增强已有对象的功能,因此具备的功能和已有类是相同的,只是进行了增强,所以装饰类和被装饰类通常是属于一个直接体系中。也即继承自同一个父类或者接口。
四、享元模式
由小的具体共同属性的对象变成一个对象,它使用共享对象,用来尽可能减少内存使用量。例如:Integer 就从-128到127是同一个对象。数组中的字符串也会这样处理
String[] arr ={"a","a"};
System.out.println(arr[0]==arr[1]);//此处相等
Integer i1 =127;
Integer i2 =127;
Integer i3 =128;
Integer i4 =128;
System.out.println(i1==i2);//此处是相等的
System.out.println(i4==i3);//此处不相等
五、代理模式
1、面向方面编程(AOP):
系统中可能存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:
安全 事务 日志
StudentService------|----------|------------|-------------
CourseService ------|----------|------------|-------------
MiscService------|----------|------------|-------------
AOP(Aspect oriented program ),面向方面的编程的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:
------------------------------------------------------切面
func1 func2 func3
{ { {
.... .... ......
} } }
------------------------------------------------------切面
使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
2、代理:
应用程序除了当前类能提供的功能外,有时还需要一些其他的额外功能,比如安全、日志等,这时就需要使用代理类。
3、创建代理的方法
Proxy类和InvacationHandler接口提供了生成动态代理的功能。
1.getProxyClass()方法:创建动态代理类的字节码。
Proxy.getProxyClass(loader, interfaces):接收两个参数,loader指定动态代理类的类加载器,interfaces指定动态代理类要实现的接口。返回Class对象。
2.newProxyInstance()方法:创建动态代理类的示例。
Proxy.newProxyInstance(loader, interfaces, h) :接收三个参数,loader指定动态代理类的类加载器,interfaces指定动态代理类要实现的接口,
h指定 与动态代理类关联的InvacationHandler对象。
一个简单调用代理的代码示例:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collection; public class ProxyDemo { public static void main(String[] args) { Collection proxy3 = (Collection)Proxy.newProxyInstance( Collection.class.getClassLoader(), new Class[]{Collection.class}, new InvocationHandler() { ArrayList target = new ArrayList(); public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long beginTime = System.currentTimeMillis(); Object retVal = method.invoke(target, args); long endTime = System.currentTimeMillis(); System.out.println(method.getName()+" runing time of "+(endTime-beginTime)); return retVal; } } ); proxy3.add("zxx"); proxy3.add("lhm"); proxy3.add("bxd"); System.out.println(proxy3.size()); } }
3、使用代理时的注意事项:
3.1、JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
3.2、CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。 3.3、InvocationHandler类里面只复写了Object里面的equals,toString,hashCode方法。
3.4、代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
<1>.在调用目标方法之前
<2>.在调用目标方法之后
<3>.在调用目标方法前后
<4>.在处理目标方法异常的catch块中
3、动态代理的工作原理:
当调用代理的各个方法时,会把请求传递给由代理类传递进来的InvacationHandler对象。
InvacationHandler对象通过invoke()方法把请求分给目标对象的各个方法。图例:

posted on 2013-01-03 20:39 Xiaobin.Wang 阅读(213) 评论(0) 收藏 举报
浙公网安备 33010602011771号