在外部类中调用某一个实例的成员方法的方式:
1.面向具体类:创建一个该类的实例对象,通过该实例对象调用其成员方法
优点:编写时简单
缺点:高耦合,后期h代码维护难,外部类严重依赖该类,当该类代码有变动时,除了改动该类的代码,在外部类所有使用了该类的代码都要改变,如A.a()要换成B.b()时,每一处与A相关的代码都要变动
//面向具体类,C与A高耦合,当要维护代码时所有与A.a()相关的代码都要改动
public class C {
public static void main(String[] args){
A a = new A();//这里要变成B b = new B();
A.a();//改动后这里要变成B.b()
...
A.a();////改动后这里要变成B.b()
}
}
public class A(){
public static void a(){
....
}
}
public class B(){
public static void b(){
....
}
}
2.部分面向接口:创建一个接口,创建该实现类,把实现类向上转型成接口,通过接口调用方法
优点:较低耦合,当要换成B类时,直接B类实现该接口进而实现某个方法,把A实现类换成B实现类就可以,改动外部类创建实例对象时的部分代码即可,如A,B同时实现D接口,在某个外部类中,把原来的D d = new A(),换成D d = new B()即可
缺点:虽然相对于第一种方法很大程度上降低了耦合,但是依然有一定程度的耦合性
//部分面向接口,较低耦合,只需要改变创建的实例就可以
public class C {
public static void main(String[] args){
D d = new A();//只要变成 D d = new B()即可
d.test();
...
...
d.test();
}
}
public class A implements D{
@Override
public void test(){
//与A相关的代码
}
}
public class B implements D{
@Override
public void test(){
//与B相关的代码
}
}
3.完全面向接口:通过容器管理对象,使用接口自定义注入实现类
优点:进一步降低了耦合,当要换成B类时,直接B类实现该接口,把创建权交给容器,由容器注入实现类,完全不需要改动外部类的代码,如Spring容器
缺点:当一个接口有多个实现类时,容器不知道注入哪个实现类,需要自己手动指定,SpringMVC常用的解决方法是遍历所有的实现类,如XXX解析器,来匹配合适的实现类
//完全面向接口
public class C {
//容器管理对象,,当要使用B时,把A实现类删掉即可自动注入实现类
@Autowired
private D d;
/*
* @Resource(name="b")//若不删除A类,当有多个实现类时,指定要注入的对象即可,也可
*@Autowired与 @Qualifer(value="b")两个注解组合指定实现类
private D d;
*/
public static void main(String[] args){
d.test();
...
...
d.test();
}
}
public class A implements D{
@Override
public void test(){
//与A相关的代码
}
}
public class B implements D{
@Override
public void test(){
//与B相关的代码
}
}
在经典的三层架构Web展示层,Buiness业务逻辑层,Dao持久层,为了降低三层的耦合性,大多使用面向抽象原则编程。面向抽象的体现之一是面向接口编程,如Service类中引用Dao接口,通过Dao接口调用接口方法,那么Service就不再强依赖于Dao接口的实现,从而降低Service层与Dao层的耦合度。(变成了依赖Dao接口,但是接口是抽象的,代码易于维护),在每一层里也是可以使用接口或者继承来实现解耦的,如业务层中,有大量的Service接口与Service实现类,持久层中的Mybatis框架的Dao接口与其运行时生成的代理实现类,Web层也大量使用面向抽象的编程思想,如SpringWebMvc的ioc容器等
简而言之:实际开发中,原则都是面向抽象开发,大多数面向接口或者面向继承等方法进行解耦,即每一层都有自己的接口,实现类或父类等,通过实现接口,继承父类等来把耦合度降低。大多数框架使用工厂模式同时运用接口,继承等方式进行解耦的,当然实际开发中也会运用到工厂模式