面向对象的设计原则(1)

一、概况

1、面向对象的关键字

  •   类、对象、引用、属性、方法
  •   接口、抽象类
  •       继承、组合
  •       私有、包、共有
  •       ……

2、面向对象编程

  •   一切皆是对象
  •   程序是对象的集合,它们通过发送消息告诉彼此该做什么
  •       每个对象都有自己的属性(存储)
  •       每个对象都拥有类型
  •       某一特性类型的所有对象都可以接受同样的消息

  总结起来:状态、行为、标示

  状态(属性)、行为(方法)、标示(说明了我们不同的类型区别)

3、面向对象的原则

 

  •   高内聚、低耦合  (高内聚是指:一个组件内部是一个非常紧密的集合
  •   将变化与不变分开
  •       依赖抽象,而不是依赖具体
  •       多用组合、少用继承
  •       开放-关闭原则
  •   ……

二、面向对象的设计实例

1、New的不足

 1 package objectoriented.coupling1;
 2 
 3 public class Example {
 4     public static void main(String[] args) {
 5         Television tv = new Television();
 6         tv.play();
 7     }
 8 }
 9 
10 class Television {
11     Panel panel = new LcdPanel();
12     
13     void play() {
14         panel.display();
15     }
16 }
17 
18 
19 interface Panel {
20     abstract public void display(); //接口定义的方法默认是public的
21 }
22 
23 class LcdPanel implements Panel {
24     int size = 32;
25     
26     LcdPanel() {
27     }
28     
29     LcdPanel(int size) {
30         this.size = size;
31     }
32     
33     public void display() {
34         System.out.println("尺寸=" + size + ",现在开始播放...");
35     }
36 }
new的不足:
  •   (1)产生直接依赖
  •   (2)只能通过参数列表区分对象的创建 —— 传入一个参数,不好理解
  •        (3)完全确定了对象的具体类型 ——
      电视机这个类 写死了具体实现,以后想换成其它面板不好维护
           private Panel panel = new LcdPanel();
  •        (4)必定产生新的对象——比如单例模式,就并不需要每次都创建新的对象

改进1——静态工厂

 

 1 package objectoriented.coupling2;
 2 
 3 
 4 public class Example {
 5     public static void main(String[] args) {
 6         Television tv = new Television();
 7         tv.play();
 8     }
 9 }
10 
11 class Television {
12     //Panel panel = new LcdPanel();
13     private Panel panel = PanelFactory.createLcdPanel();
14     
15     void play() {
16         panel.display();
17     }
18 }
19 
20 //任何问题都可以增加一个中间层来解决;
21 //好处,电视剧要变化面板时,只需要变化面板工厂类
22 class PanelFactory {
23     public static Panel createLcdPanel() { //Panel类型,但返回的是子类对象
24         return new LcdPanel();
25     }
26     
27     public static Panel createCrtPanel() {
28         return new CrtPanel();
29     }
30 }
31 
32 
33 interface Panel {
34     abstract public void display();
35 }
36 
37 class LcdPanel implements Panel {
38     int size = 32;
39     
40     LcdPanel() {
41     }
42     
43     LcdPanel(int size) {
44         this.size = size;
45     }
46     
47     public void display() {
48         System.out.println("尺寸=" + size + ",LcdPanel现在开始播放...");
49     }
50 }
51 
52 class CrtPanel implements Panel {
53     int size = 32;
54     
55     CrtPanel() {
56     }
57     
58     CrtPanel(int size) {
59         this.size = size;
60     }
61     
62     public void display() {
63         System.out.println("尺寸=" + size + ",CrtPanel现在开始播放...");
64     }
65 }
原理:增加一个中间层来专门处理对象的依赖,相互依赖的对象可以分别演进(无相互依赖),只需要维护和演进中间层即可
 
静态工厂的优势:
  •   (1)将变化控制在较小的范围——具体的依赖转移到了工厂方法
  •   (2)更有意义的名字
  •       (3)可以返回任何子类型的对象
  •   (4)可以重用对象——可以重用时,在静态工厂里面用一个属性缓存起来,下次调用直接返回

 静态工厂的不足:

    •   (1)创建对象的方法与其它静态方法没有区别 ???
    •   (2)名门惯例——valueOf()、getInstance()、newInstance()、getXxx()、newXxx(),

从静态方法看不出来“这是一个创建对象的方法”,只有名字取得比较像时才可以看出来

 

改进2——使用环境变量

 

 1 package objectoriented.coupling3;
 2 
 3 
 4 public class Example {
 5     public static void main(String[] args) {
 6 //        System.setProperty("panel.impl.class", "objectoriented.coupling3.CrtPanel");
 7 //      一般通过参数,让Java程序运行时得到
 8         System.setProperty(args[0], args[1]);
 9         
10         Television tv = new Television();
11         tv.play();
12     }
13 }
14 
15 class Television {
16     //Panel panel = new LcdPanel();
17     private Panel panel = PanelFactory.createPanel();
18     
19     void play() {
20         panel.display();
21     }
22 }
23 
24 //任何问题都可以增加一个中间层来解决;
25 //好处,电视剧要变化面板时,只需要变化面板工厂类
26 class PanelFactory {
27     private static final String DEFAULT_PANEL = "objectoriented.coupling3.LcdPanel";
28     
29     public static Panel createPanel() {
30         String panel = System.getProperty("panel.impl.class"); //获取系统变量
31         if (panel == null) {
32             panel = DEFAULT_PANEL;
33         }
34         try {
35             Class<?> panelClass = PanelFactory.class.getClassLoader()
36                     .loadClass(panel);  //通过参数配置不同,使得产生的实际面板对象不同
37             return (Panel)panelClass.newInstance();
38         } catch (Exception e) {
39             throw new RuntimeException(e);
40         }
41     }
42 }
43 
44 
45 interface Panel {
46     abstract public void display();
47 }
48 
49 class LcdPanel implements Panel {
50     int size = 32;
51     
52     LcdPanel() {
53     }
54     
55     LcdPanel(int size) {
56         this.size = size;
57     }
58     
59     public void display() {
60         System.out.println("尺寸=" + size + ",LcdPanel现在开始播放...");
61     }
62 }
63 
64 class CrtPanel implements Panel {
65     int size = 32;
66     
67     CrtPanel() {
68     }
69     
70     CrtPanel(int size) {
71         this.size = size;
72     }
73     
74     public void display() {
75         System.out.println("尺寸=" + size + ",CrtPanel现在开始播放...");
76     }
77 }

 

设置main方法的参数:

原理:利用环境变量来实现,环境变量(全局类名)调用System.setProperty(key, value),中间处理层再System.getProperty(key)取出全局类名,并通过反射加载类,把实例化的对象返回

使用环境变量的优势:可以灵活的通过启动配置改变接口的实现

不足:需要使用反射,即反射的不足

说明:

39             throw new RuntimeException(e);

/*
这里如果throw new Exception(e); 该类异常属于必须要处理的异常,必须交给上一级处理,
即static Panel createPanel() throws Exception,同时Panel panel = PanelFactory1.createPanel()会报如下错误
* Default constructor cannot handle exception type Exception thrown by implicit super constructor. Must define an
explicit constructor
——如果改为throw new RuntimeException(e),则本方法不需要throws异常,因为RuntimeException会交给JVM处理,直接终止程序
*/

 

改进3——全局注册表

 

 1 package objectoriented.coupling4;
 2 
 3 import java.util.HashMap;
 4 import java.util.Map;
 5 
 6 
 7 public class Example {
 8     public static void main(String[] args) {
 9         GlobalRegistry.registerServices("panel", new LcdPanel()); //业务调用前,先注册  JNDI???最后2min
10         
11         Television tv = new Television();
12         tv.play();
13     }
14 }
15 
16 
17 class Television {
18     private Panel panel = (Panel)GlobalRegistry.getService("panel");  //通过名字,取出来(即使是在别的地方注册的)
19     
20     void play() {
21         panel.display();
22     }
23 }
24 
25 class GlobalRegistry {
26     private static final Map<String, Object> services = new HashMap<String, Object>();
27     
28     public static void registerServices(String serviceName, Object service) {
29         services.put(serviceName, service);  //如果services非静态,则会报Cannot make a static reference to the non-static field services
30     }
31     
32     public static Object getService(String serviceName) {
33         return services.get(serviceName);
34     }
35 }
36 
37 
38 interface Panel {
39     abstract public void display();
40 }
41 
42 class LcdPanel implements Panel {
43     int size = 32;
44     
45     LcdPanel() {
46     }
47     
48     LcdPanel(int size) {
49         this.size = size;
50     }
51     
52     public void display() {
53         System.out.println("尺寸=" + size + ",LcdPanel现在开始播放...");
54     }
55 }
56 
57 class CrtPanel implements Panel {
58     int size = 32;
59     
60     CrtPanel() {
61     }
62     
63     CrtPanel(int size) {
64         this.size = size;
65     }
66     
67     public void display() {
68         System.out.println("尺寸=" + size + ",CrtPanel现在开始播放...");
69     }
70 }
原理:也是利用全局map(静态变量),map里是一个new的对象,在主方法里先注册(即new对象后put进map),需要的时候再从map里取(get)出来
 
全局注册表的优势:
  •   服务提供者和消费者通过注册表关联
  •   可以由注册表负责对象的创建
  •       J2EE大量使用此方式
JNDI  就是一个大的全局Map,tomcat配置一个资源,比如实例化一个连接池,把这个资源通过一个名词绑定到容器中,然后再容器中可以部署不同的war包;war包启动时,通过这个名字在容器中取出来自己需要的资源,比如说连接池,就拿到连接池了
 
连接池的初始化要知道数据库的账号、密码、地址等,易变的,由程序直接写死,要重新编译,扔进去war包等;
通过这种方式,war包不需要更改,只需要在配置文件中更改账号、密码、地址等,可以直接使程序生效
 
 
 
都是一种数据组织的一种方式,这几种都是 主动依赖(主动去索取对象)  业务的东西都需要依赖框架的东西
 
1、如果依赖的框架坏了
2、或者要由静态工厂 改为 环境变量   这两种情况都需要改变
 
为了解决上述问题,引入  对象注入

posted on 2013-11-23 17:39  gogoy  阅读(259)  评论(0编辑  收藏  举报

导航