当和朋友谈到AOP时,第一映像会说AOP的应用层面,比如拦截器、过滤器,实现复用等等使用层面上。
这些应用层面上的回答远远没有深入AOP的思想。是的,AOP是思想,面向切面思想。
既然是编程思想,那我们就有必要了解AOP的整个过程,以及AOP思想出现的原因。
AOP面向切面编程思想,能够解决什么问题,我们回顾一下编程思想的发展路线......
早期的POP面向过程编程,即是以功能为中心来进行思考和组织的一种编程方法,强调的是功能。
分析解决问题所需要的步骤,然后用函数把这些步骤一一实现,使用的时按顺序依次调用,严格按照顺序,侧重解决步骤,着眼局部或具体。
实际是一种单一的思考方式,符合人类的思考方式,是一种基础的方法,从实际出发。
它能够流程化编程任务,只需要考虑实现方式和最终结果;开发效率高,代码短小精悍,善于结合数据结构来开发高效率的程序;明确流程,步骤清楚,便于节点分析。
但是,需要深入思考,比较耗费精力;代码复用性低,不易扩展,维护难度大,且面向过程的模块化难度较高,耦合度也高。
由此可见,随着时代发展,面对的系统逐渐复杂,一般的POP无法满足,于是出现了OOP面向对象编程思想。
需要说明的是,并不是因为OOP的出现,才使得POP没有余地,或许有不少的伙伴甚至没听过POP。POP和OOP其实是一种互补关系,相关复杂问题拆解之后还是会回归到面向过程的思想。
OOP,面向对象编程,以对象为中心,复杂系统出现时,盛行的一种新型的程序设计思想。以对象模型为基础进行的抽象过程,并在应用过程中描述自己的抽象概念定义,包括对象、类、封装、继承等语法和概念。
通常脱口而出“万物皆对象”,它可以搭建大型的复杂的系统,它是将数据抽象为模块结构,然后必须存在某种方式来实现代码的多态执行,最后它能压缩部分代码和函数。
或许不是很好理解。
举个例子,比如说:我们的系统是一个建筑,那类/对象就是砖块,砖块能够组成墙,墙能构成房间,房间组合在一起就组成了一栋建筑。
这是不是和我们面向对象编程时是一样的?一个模块由多个类共同实现,模块又组成了某项服务,多个服务构成了一个完整的系统。
那么,POP能够满足精简系统的开发,OOP能够满足复杂的系统,为什么还会出现AOP呢?
首先,构建了一个系统之后,依然会有需求的存在,有了需求就会难免调整代码,那么类就会发生变化,如果大规模变化将是不现实的,整个系统就会存在隐患以及人力物力的多余需求。
类应该是固定的,不应该频繁更改,之所以出现了那么多的设计原则和设计模式,目的就是针对不断的需求变化而进行拆解分离,使我们的类能够像砖块一样固定,从而让系统稳健。
我们设计完一个系统之后,新增需求要增加一个日志打印模块,授权模块,如果在各个模块都添加的话改动的工作量无疑是很大的,存在的隐患也是很大的。
从对象的组织角度来讲,类是继承关系,适合纵向扩展,这也是OOP的思想。
就像我们的建筑,打好地基稳固之后,纵向增加楼层是相对较方便的,但是横向扩展是很困难的。
因此,面向对象设计有两个缺陷:
1.共性问题,面向对象设计一般是纵向思维,总会有一些共性不足。
2.扩展问题,当我们需要对现有的对象动态的新增某些行为或责任时就会变得比较困难。
于是,AOP出现了,来弥补OOP的共性问题和扩展问题。
当然,AOP不是OOP的升级版,是对OOP缺陷的补充。
POP,OOP,AOP 这三种编程方式,本质也是互相弥补,从来不是哪个盛行而其他的就需要了。
那么到底什么是AOP呢?
AOP面向切面编程,切面就是横切关注点模块化,OOP是使用类状态(属性)和行为模块化。
类和接口组织的,是针对横向关注点,跨越应用程序的多个模块的功能需求。
AOP涉及的应用,例如:日志记录、性能统计、安全控制,异常处理。都是可以从业务代码中划分出来,非业务逻辑的方法。
AOP善于将公共的功能提取出来,成立公共的模块,只关注通用的功能,不关注业务逻辑。
AOP的优势:
1.将通用的功能从业务逻辑中抽离出来,提高代码的复用性,有利于后期的维护和扩展。
2.在软件设计时,抽出通用功能,有利于软件设计的模块化,降低架构的复杂度。
AOP与 OOP所针对的目标是不同的。OOP针对业务处理过程的实体、属性,行为进行抽象封装。
AOP则是针对业务处理过程中切面进行提取,就是面向对象过程中的某个步骤或阶段的提取,来降低逻辑过程中各个部分的耦合。
因此,相比于OOP,可以总结出AOP的特点。
1.面向目标不同,OOP面向名词,AOP面向动词。
2.思想结构不同,OOP是纵向,AOP是横向。
3.注重方面不同,OOP偏重业务逻辑单元划分,AOP偏重业务处理过程中的某个步骤。
那么,我们在AOP应用层面的实现,有两种常见的方式。
静态代理AOP,就是手写代码。
我们以装饰器模式为例子,装饰器就是在一个原有对象的情况下封装一个新的装饰器类,来包装原有类,提供额外的功能。
//接口服务
public interface ITravelService
{
Task PlanToTravel(Travel travel);
}
public class TravelService : ITravelService
{
public Task PlanToTravel(Travel travel)
{
Console.WriteLine($"旅客{travel.name}成功加入旅行团");
return Task.CompletedTask;
}
}
//装饰器
public class TravelDecorator : ITravelService
{
private readonly ITravelService _travelService;
public TravelDecorator(ITravelService travelService)
{
_travelService = travelService;
}
public async Task PlanToTravel(Travel travel)
{
//旅行前
await IsAllowToTravel(travel);
await _travelService.PlanToTravel(travel);
//旅行后
await IsAllowBack(travel);
}
public Task IsAllowToTravel(Travel travel)
{
Console.WriteLine($"目前处于疫情严重,将停止旅游!");
return Task.CompletedTask;
}
public Task IsAllowBack(Travel travel)
{
Console.WriteLine($"返回目标地处于风控期,暂时无法返航!");
return Task.CompletedTask;
}
2.动态代理实现,可以通过反射来实现。
例如现有的AOP框架,Romoting(分布式通讯框架)、Castle轻量级容器(包含实现ORM、IOC、MVC,AOP)、Unity(IOC容器,AOP)。
以 Remoting和Castle 分别写一个实例,如下:
//Remoting 例子
public class DynamicProxy<T> : DispatchProxy where T : class
{
public T Target { get; set; }
public DynamicProxy(){}
//执行之前执行逻辑
public Action BeforAction { get; set; }
//执行之后执行逻辑
public Action AfterAction { get; set; }
public static T Decorate(T target, Action BeforAction, Action AfterAction)
{
// DispatchProxy.Create creates proxy objects
var proxy = Create<T, DynamicProxy<T>>() as DynamicProxy<T>;
proxy.AfterAction = AfterAction;
proxy.BeforAction = BeforAction;
// If the proxy wraps an underlying object, it must be supplied after creating the proxy.
proxy.Target = target ?? Activator.CreateInstance<T>();
return proxy as T;
}
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
//执行业务之前的代码逻辑
BeforAction();
var result = targetMethod.Invoke(Target, args);
//执行业务之后的代码逻辑
AfterAction();
return result;
}
}
//remoting 实例 使用方式
var tralvel = new Travel
{
name = "张三",
date = DateTime.Now,
telphone = "110",
travel_target = "HangZhou"
};
var service = new TravelService();
var proxyService = DynamicProxy<ITravelService>.Decorate(service,
() => { Console.WriteLine("执行逻辑前"); },
() => { Console.WriteLine("执行逻辑后"); });
proxyService.PlanToTravel(tralvel);
//castle 实例
public class CastleProxy<T> : IInterceptor where T : class
{
private static readonly ProxyGenerator _generator = new ProxyGenerator();
public T Target { get; set; }
public CastleProxy(){}
//执行之前
public Action BeforAction { get; set; }
//执行之后
public Action AfterAction { get; set; }
public static T Decorate(T target, Action BeforActions, Action AfterActions)
{
var proxy = target != null ?
_generator.CreateInterfaceProxyWithTarget(target, new CastleProxy<T>()
{
BeforAction = BeforActions,
AfterAction = AfterActions
}):
_generator.CreateInterfaceProxyWithTarget<T>(Activator.CreateInstance(typeof(T)) as T,new CastleProxy<T>()
{
BeforAction = BeforActions,
AfterAction = AfterActions
});
return proxy;
}
public void Intercept(IInvocation invocation)
{
try
{
BeforAction();
invocation.Proceed();//程序执行
AfterAction();
}
catch (TargetInvocationException error)
{
throw error.InnerException;
}
}
}
//castle 实例 使用方式
var tralvel = new Travel
{
name = "张三",
date = DateTime.Now,
telphone = "110",
travel_target = "HangZhou"
};
var service = new TravelService();
var proxyService = CastleProxy<ITravelService>.Decorate(service,
() => { Console.WriteLine("执行逻辑前"); },
() => { Console.WriteLine("执行逻辑后"); });
proxyService.PlanToTravel(tralvel);
好了,以上就是AOP面向切面编程思想的内容了,希望我讲解的内容能够帮到大家。哈,希望伙伴们给个小心心!
我正在写一篇关于在Ruby中几乎一切都是对象的博客文章,我试图通过以下示例来展示这一点:classCoolBeansattr_accessor:beansdefinitialize@bean=[]enddefcount_beans@beans.countendend所以从类中我们可以看出它有4个方法(当然,除非我错了):它可以在创建新实例时初始化一个默认的空bean数组它可以计算它有多少个bean它可以读取它有多少个bean(通过attr_accessor)它可以向空数组写入(或添加)更多bean(也通过attr_accessor)但是,当我询问类本身它有哪些实例方法时,我没有看到默认
如标题所示,我正在尝试使用Rspec测试自定义验证器。我得到一个错误,我不明白为什么......如果你能阐明一些问题,我将非常感激。我们开始吧:验证者规范require'spec_helper'describeGraphDateValidatordoit"shouldnotvalidateactivitywithemptystarttime"doexpect{Graph.new({start_time:''}).valid?}.toeq(false)endend如果我打印Graph.new({start_time:''}).valid?它会打印false然而,当它通过规范时,它返回一个
这个问题在这里已经有了答案:Rubymetaclassconfusion(4个答案)关闭7年前。我对Ruby对象模型不太了解。首先,Ruby中的一切都是Class的实例吗??这些都产生true:pObject.instance_of?(Class)pClass.instance_of?(Class)pModule.instance_of?(Class)pBasicObject.instance_of?(Class)classHello;endpHello.instance_of?(Class)我不太明白这怎么可能,如果Object是Class的父类(superclass),它怎么可能都
我正在尝试绕过rails配置这个极其复杂的迷宫。到目前为止,我设法在ubuntu上设置了rvm(出于某种原因,ruby在ubuntu存储库中已经过时了)。我设法建立了一个Rails项目。我希望我的测试项目使用mysql而不是mysqlite。当我尝试“rakedb:migrate”时,出现错误:“!!!缺少mysql2gem。将其添加到您的Gemfile:gem'mysql2'”当我尝试“geminstallmysql”时,出现错误,告诉我需要为安装命令提供参数。但是,参数列表很大,我不知道该选择哪些。如何通过在ubuntu上运行的rvm和mysql获取rails3?谢谢。
所以我在Ruby方面几乎是个新手,我整理了一个代码来解决MinCut问题(对于一个作业,是的——我整理并测试了那部分代码),并且我无法弄清楚如何读取文件并将其放入数组数组中。我有一个文本文件要阅读,其中包含不同长度的列,如下所示137791642123134348123134109我想将它读入一个二维数组,其中每一行和每一列都被拆分,每一行都进入一个数组。因此,上述示例的结果数组将是:[[1,37,79,164],[2,123,134],[3,48,123,134,109]]我读取文本文件的代码如下:defread_array(file,count)int_array=[]File.f
我无法理解发生了什么,当我请求索引操作时,我收到了ActionController::UnknownFormat,甚至在views/products文件夹中存在一个index.js文件。任何人都可以帮我弄清楚我错过了什么?我正在使用Rails4和Ruby2.0.0-p247。classProductsController 最佳答案 您应该在请愿书上指定格式,即:获取:show,:user_id=>user_id,:format=>:json 关于ruby-on-rails-ActionC
使用Ruby1.9.2,我在IRB中有以下Ruby代码:>r1=/^(?=.*[\d])(?=.*[\W]).{8,20}$/i>r2=/^(?=.*\d)(?=.*\W).{8,20}$/i>a=["password","1password","password1","pass1word","password1"]>a.each{|p|puts"r1:#{r1.match(p)?"+":"-"}\"#{p}\"".ljust(25)+"r2:#{r2.match(p)?"+":"-"}\"#{p}\""}这会产生以下输出:r1:-"password"r2:-"password"r1:
给定以下内容,如何获取URL的完整路径uri=URI("http://foo.com/posts?id=30&limit=5#time=1305298413")我只想要posts?id=30&limit=5#time=1305298413我试过uri.path并返回/posts和ui.query返回'id=30&limit=5' 最佳答案 您要找的方法是request_uriuri.request_uri=>"/posts?id=30&limit=5"如果需要,您可以使用任何您想要删除前导/的方法。编辑:要获取#符号后的部分,请使用
我对在Rails应用程序中使用Elasticsearch还很陌生,我正在使用耐嚼的gem来实现它。当我通过railss在开发模式下运行我的Rails应用程序时,我然后通过elasticsearch命令运行Elasticsearch并运行rakechewy:reset:all为我的数据创建索引。这样做一切正常,但如果我重新启动服务器,我必须再次运行rakechewy:reset:all命令以重建索引,否则我会收到错误消息。当我重新启动服务器时,索引会发生什么变化?服务器停止时是否销毁?我不是很熟悉Elasticsearch的功能,所以希望有人能对幕后发生的事情有所了解。
$gem--version[/home/rohit/.rvm/gems/ruby-1.9.3-p125@qnrDashboard/specifications/net-ssh-2.5.2.gemspec]isn'taGem::Specification(NilClassinstead).[/home/rohit/.rvm/gems/ruby-1.9.3-p125@qnrDashboard/specifications/net-sftp-2.0.5.gemspec]isn'taGem::Specification(NilClassinstead).[/home/rohit/.rvm/ge