2.1.1单例模式
public class NewSingleton {
private NewSingleton() {
}
private static class NewSingletonHolder {
private static NewSingleton instance = new NewSingleton();
}
public NewSingleton getInstance() {
return NewSingletonHolder.instance;
}
}
使用内部类的方式实现单例,既可以做到延迟加载,也不必使用同步关键字,是一种比较完善的方式。
public class SerSingleton implements Serializable {
private static final long serialVersionUID = 6978432124038848244L;
String name;
private SerSingleton() {
System.out.println("Singleton is create");
name = "SerSingleton";
}
private static SerSingleton instance = new SerSingleton();
public static SerSingleton getInstance() {
return instance;
}
public static void createString() {
System.out.println("createString is Singleton");
}
// 阻止生成新的实例,总是返回当前对象---必须是这个方法名
private Object readResolve() {
return instance;
}
}
public class SerSingletonTest {
@Test
public void test() throws Exception {
SerSingleton s1 = null;
SerSingleton s = SerSingleton.getInstance();
// 先将实例串行化到文件
FileOutputStream fos = new FileOutputStream("SerSingleton.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s);
oos.flush();
oos.close();
// 从文件读出原有的单例类
FileInputStream fis = new FileInputStream("SerSingleton.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
s1 = (SerSingleton) ois.readObject();
Assert.assertEquals(s, s1);
}
}
2.1.2代理模式
代理模式可以用于多种场合,如用于远程调用的网络代理,考虑安全因素的安全代理等。延迟加载只是代理模式的一种应用场景。
延迟加载的核心思想是:如果当前并没有使用这个组件,则不需要真正的初始化它。使用一个代理对象替代它原来的位置,
只要在真正需要使用的时候,才对它进行加载。
-------------------------------------------------------------------------------------------
public interface IDBQuery{
String request(); //主题接口
}
//真实对象,重量级,构造会比较慢
public class DBQuery implements IDBQuery{
public DBQuery(){
try{
Thread.sleep(1000);
}catch(InterruptedException e){ e.pringStackTrace(); }
}
@Override
public String request(){
return "request string";
}
}
//代理类,轻量级,创建很快,用于替代DBQuery
public class DBQueryProxy implements IDBQuery{
private DBQuery real = null;
@Override
public String request(){
//在真正需要的时候,才创建真实对象,创建过程可能很慢
if(real == null){
real = new DBQuery();
return real.request();
}
}
}
public class Main{
public static void main(String[] args){
IDBQuery q = new DBQueryProxy();
q.request();//在真正使用时才创建真实对象
}
}
//将代理模式用户实现延迟加载,可以有效地提升系统的启动速速,对改善用户体验有很大的帮助。
-------------------------------------------------------------------------------------
动态代理,java自带的 代替上面的DBQueryProxy
public class JdkDbQueryHandler implements InvocationHandler{
IDBQuery real = null;
@Overiride
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
if(real == null){
real = new DBQuery();
return real.request();
}
}
public static IDBQuery createJdkProxy(){
IDBQuery jdkProxy = (IDBQuery) Proxy.newProxyInstance(
ClassLoader.getSystemClassLoader(),new Class[]{IDBQuery.class},new JdkDbQueryHandler());
return jdkProxy;
}
}
------------------------------------------------------------------------------------
cglib实现的代理
public class CglibDbQueryInterceptor implements MethodInterceptor{
IDBQuery real = null;
@Override
public Object intercept(Object arg0,Method arg1,Object[] arg2,MethodProxy arg3)
throws Throwable{
if(real == null)
real = new DBQuery();
return real.request();
}
public static IDBQuery createCglibProxy(){
Enhancer enhancer = new Enhancer();
enhancer.setCallback(new CglibDbQueryInterceptor());//置顶切入器,定义代理类逻辑
enhancer.setInterfaces(new Class[] {IDBQuery.class}); //指定实现的接口
IDBQuery cglibProxy = (IDBQuery)enhancer.create();
return cglibProxy;
}
}
-----------------------------------------------------------------------------------------
使用Javassist生成动态代理可以使用两种方式:一种是使用代理工厂创建,另一种通过使用动态代码创建。
代码略,具体用到可以参考网络资源。
结论:就动态代理的方法调用性能而言,CGLIB和Javassist的机遇动态代码的代理都优于JDK自带的动态代理。
此外,JDK的动态代理要求代理类和真实主题都实现同一接口,而另外两个则无此强制要求。
-------------------------------------------------------------------------------------------
Hibernate框架中对实体类的动态代理是代理模式用于延迟加载的经典实现,有兴趣的读者,可以深入研究下。
Hibernate的延迟加载主要有2种:1是属性的延迟加载,2是关联表的延迟加载。
2.1.3享元模式
享元模式是为数不多的,只为提升系统性能而生动的设计模式。它的主要作用就是复用大对象(重量级对象),
以节省内存空间和对象创造时间。
享元模式和对象池的最大不同在于:享元对象是不可相互替代的,它们各自都有各自的含义和用途;而对象池
中的对象都是等价的,如数据库连接池中的数据库连接。
public interface IReportManager{
public String createReport();
}
public class FinancialReportManager implements IReportManager{
//财务报表
protected String tenantId = null;
public FinancialReportManager(String tenantId){
this.tenantId = tenantId;
}
@Override
public String createReport(){
return "This is a financial report";
}
}
public class EmployeeReportManager implements IReportManager{
//员工报表
protected String tenantId = null;
public EmployeeReportManager(String tenantId){
this.tenantId = tenantId;
}
@Override
public String createReport(){
return "This is a employee report";
}
}
//最为核心的享元工厂类如下,它是享元模式的精髓所在。
public class ReportManagerFactory{
Map<String,IReportManager> financialReportManager =
new HashMap<>();
Map<String,IReportManager> employeeReportManager =
new HashMap<>();
IReportManager getFinancialReportManager(String tenantId){
IReportManager r = financialReportManager.get(tenantId);
if(r==null){
r = new FinancialReportManager(tenantId);
financialReportManager.put(tenantId,r);//维护已创建的享元对象
}
return r;
}
IReportManager getEmployeeReportManager(String tenantId){
IReportManager r = employeeReportManager.get(tenantId);
if(r==null){
r = new EmployeeReportManager(tenantId);
employeeReportManager.put(tenantId,r);//维护已创建的享元对象
}
return r;
}
}
public class Main{
public static void main(String[] args){
ReportManagerFactory rmf = new ReportManagerFactory();
IReportManager rm = rmf.getFinancialReportManager("A");
System.out.println(rm.createReport());
}
}
装饰者模式可以有效分离性能组件和功能组件,从而提升模块的可维护性并增加模块的复用性。
装饰者(Decorator)和被装饰者(ConcreteComponment)拥有相同的接口(Componment)。
被装饰者通常是系统的核心组件,完成特定的功能目标。
而装饰者则可以在被装饰者的方法前后,加上特定的前置处理和后置处理,增强被装饰者的功能。
public interface IPacketCreator{
public String handleContent(); //用于内容处理
}
public class PacketBodyCreator implements IPacketCreator{
@Override
public String handleContent(){
return "构造核心数据,但不包含格式";
}
}
public abstract class PacketDecorator implements IPacketCreator{
IPacketCreator componment;
public PacketDecorator(IPacketCreator componment){
this.componment = componment;
}
}
public class PacketHTMLHeaderCreator extends PacketDecorator{
public PacketHTMLHeaderCreator(IPacketCreator componment){
super(c);
}
@Override
public String handleContent(){
StringBuilder sb = new StringBuilder();
sb.append("<html>");
sb.append("<body>");
sb.append(componment.handleContent());
sb.append("</body>");
sb.append("</html>\n");
return sb.toString();
}
}
public class PacketHTTPHeaderCreator extends PacketDecorator{
public PacketHTTPHeaderCreator(IPacketCreator componment){
super(componment);
}
@Override
public String handleContent(){
StringBuilder sb = new StringBuilder();
sb.append("Cache-Control:no-cache\n");
sb.append("Date:Mon,31Dec201412:12:12GMT\n");
sb.append(componment.handleContent());
return sb.toString();
}
}
public class Main{
public static void main(String[] args){
IPacketCreator pc = new PacketHTTPHeaderCreator(
new PacketHTMLHeaderCreator(new PacketBodyCreator()));
System.out.println(pc.handleContent());
}
}
2.1.5观察者模式
观察者模式可以用于事件监听,通知发布等场合。
可以确保观察者在不使用轮询监控的情况下,及时收到相关消息和事件。
主题接口,是指被观察的对象。
public interface ISubject{
public void attach(IObserver observer); //添加观察者
public void detach(IObserver observer); //删除观察者
public void inform();
}
public interface IObserver{
public void update(Event evt);//更新观察者
}
public class ConcreteSubject implements ISubject{
Vector<IObserver> observers = new Vector<>();
public void attach(IObserver observer){
observers.addElement(observer);
}
public void inform(){
Event evt = new Event();
for(IObserver ob:observers){
ob.update(evt); //注意,在这里通知观察者
}
}
}
public class ConcreteObserver implements IObserver{
public void update(Event evt){
System.out.println("observer receives information");
}
}
2.1.6Value Object模式
展示层 -- 业务逻辑层 -- 持久层
RMI客户端 -- RMI服务器 --- RDBMS
getClientName
getProdName
getNumber
替换成Value Object模式下,三次交互只要一次网络通信。
getOrder
使用Value Object 模式可以有效减少网络交互次数,提高远程调用方法的性能,
也能使系统接口具有更好的可维护性。
public interface IOrderManager extends Remote{
public Order getOrder(int id) throws RemoteException; //value Object 模式
public String getClientName(int id) throws RemoteException;
public String getProdName(int id) throws RemoteException;
public int getNumber(int id) throws RemoteException;
}
public class OrderManager extends UnicastRemoteObject implements IOrderManager{
protected OrderManager() throws RemoteException{
super();
}
private static final long serialVersionUID = -12120130041231254L;
@Override
public Order getOrder(int id) throws RemoteException{ //返回订单信息
Order o = new Order();
o.setClientName("billy");
o.setNumber(20);
o.setProductName("desk");
return o;
}
@Override
public String getClientName(int id) throws RemoteException{
return "billy"; //返回订单的客户名
}
@Override
public String getProdName(int id) throws RemoteException{
return "desk"; //返回商品名称
}
@Override
public int getNumber(int id) throws RemoteException{
return 20; //返回数量
}
}
public class Order implements java.io.Serializable{
private int orderId;
private String clientName;
private int number;
private String productName;
//getter setter
}
//业务逻辑层注册并开启RMI服务器:
public class OrderManagerServer{
public static void main(String[] args){
try{
LocateRegistry.createRegistry(1099); //注册RMI端口
IOrderManager usermanager = new OrderManager();//RMI远程对象
Naming.rebind("OrderManager",usermanager); //绑定RMI对象
System.out.println("OrderManager is ready.");
}catch(Exception e){
System.out.println("OrderManager Server failed: "+e);
}
}
}
客户端的测试代码如下,它分别展示了使用value object模式封装数据和不使用value object
模式的性能差异:
public static void main(String[] args){
try{
IOrderManager usermanager = (IOrderManager)Naming.lookup("OrderManager");
long begin = System.currentTimeMills();
for(int i=0;i<1000;i++){
usermanager.getOrder(i); //Value Object模式
}
System.out.println("getOrder spend:"+(System.currentTimeMills() - begin));
begin = System.currentTimeMills();
for(int i=0;i<1000;i++){
usermanager.getClientName(i); //通过多次交互获取数据
usermaneger.getNumber(i);
usermanager.getProdName(i);
}
System.out.println("2 Method call speed:"+(System.currentTimeMills() - begin));
System.out.println(usermanager.getOrder(0).getClientName());
}catch(Exception e){
System.out.println("OrderManager exception:"+e);
}
}
2.1.7业务代理模式
value Object 模式是将远程调用的传递数据封装在一个串行化的对象中进行传输,
而业务代理模式则是将一组由远程方法调用构成的业务流程,封装在一个位于展示层的代理类中。
比如,如果用户需要修改一个订单,大概步骤:
校验用户;
获取旧的订单信息;
更新订单。
展示层 -- 业务逻辑层 -- 持久层
RMI客户端 RMI服务端 RDBMS
优化前
checkUser
getOrder
updateOrder
优化方案:在展示层和业务逻辑层之间增加一个业务代理层
展示层
updateOrder-->业务代理 -- 业务逻辑层
基于远程调用封装业务逻辑 RMI服务端
checkUser
getOrder
updateOrder
业务代理模式将一些业务流程封装在前台系统,为系统性能优化提供了基础平台。
在业务代理中,不仅可以复用业务流程,还可以视情况为展示组件提供缓存等功能,从而
减少远程方法调用次数,降低系统压力。
一个未使用业务代理模式的展示层实现代码如下:
public static void main(String[] args){
try{
IOrderManager usermanager = (IOrderManager) Naming.lookup("OrderManager");
if(usermanager.checkUser(1)){
Order o = usermanager.getOrder(1);//所有的远程调用都会被执行
//当并发量较大时,严重影响性能
o.setNumber(10);
usermanager.updateOrder(o); //3个usermanager的方法调用
}
}catch(Exception e){
System.out.println("OrderManager exception:"+e);
}
}
而是用来业务代理后,展示层组件可以优化为:
public static void main(String[] args){
BusinessDelegate bd = new BusinessDelegate();
Order o = bd.getOrder(11);
o.setNumber(11);
bd.updateOrder(o); //使用业务代理完成更新订单
}
在业务代理对象BusinessDelegate中,可以增加缓存,从而直接减少远程方法调用次数
public class BusinessDelegate{
IOrderManager usermanager = null; //封装远程方法调用的流程
public BusinessDelegate(){
try{
usermanager = (IOrderManager)Naming.lookup("OrderManager");
}catch(MalformedURLException e){
e.printStackTrace();
}catch(RemoteException e){
e.printStackTrace();
}catch(NotBoundException e){
e.printStackTrace();
}
}
public boolean checkUserFromCache(int uid){
return true;
}
public boolean checkUser(int uid) throws RemoteException{
//当前对象被多个客户端共享,可以在本地缓存中校验用户
if(!checkUserFromCache(uid)){
return usermanager.checkUser(1);
}
return true;
}
public Order getOrderFromCache(int oid){
return null;
}
public Order getOrder(int oid) throws RemoteException{
//可以在本地缓存中获取订单,减少远程方法调用次数
Order order = getOrderFromCache(oid);
if(order==null){
return usermanager.getOrder(oid);
}
return order;
}
public boolean updateOrder(Order order) throws Exception{
//暴露给展示层的方法,封装了业务流程
if(checkUser(1)){//可能在缓存中执行
Order o = getOrder(1);
o.setNumber(10);
usermanager.updateOrder(o);
}
return true;
}
}