Java设计经验

从书上摘抄一部分认为比较有价值的内容:

一、编写优秀的代码需要深刻理解面向对象的设计理念和设计经验,并能自觉应用到代码设计中。
二、设计模式是针对反复出现的问题的经典解决方案,它是对特定条件(上下文)下问题的设计方案的经验总结,是前人设计实践经验的精华。
三、面向对象设计的原则是面向对象思想的提炼(即合理的抽象)。
四、耦合度是对模块间关联程度的度量。耦合强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。耦合度描述了模块间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,也表明其独立性越差。降低模块间的耦合度能减少模块间的影响,防止对某一模块修改所引起的“牵一发动全身”的连锁效应,保证系统设计顺利进行。耦合按从弱到强的顺序可分为以下5种:
(一)无耦合。模块能独立地工作,不需要其他模块的存在。
(二)数据耦合。两个模块彼此通过参数交换信息,且交换的仅仅是数据。
(三)控制耦合。一个模块通过传递开关、标志、名字等控制信息,明显地控制另一模块的功能。
(四)公共耦合。一组模块都访问同一个公共数据环境,该公共数据环境可以是全局数据结构、共享的通信区、内存的公共区。
(五)内容耦合。一个模块直接修改另一个模块的内部数据,或直接转入另一个模块。
类之间应该低耦合,但是每个类应该高内聚。内聚是表示模块、类内部聚集和关联的程度,是内部各元素之间联系的紧密程度。高内聚是指要高度的聚集和关联,因为低内聚表明类中的元素之间很少相关。
最佳实践原则如下:
1. 少使用类的继承,多用接口隐藏实现的细节。面向对象编程引入接口除了支持多态外,隐藏实现细节也是其中一个目的。
2. 避免使用全局变量。
3. 多用设计模式。
4. 尽量不用“硬编码”的方式写程序,同时也尽量避免直接用SQL语句操作数据库
5. 避免直接操作或调用其他模块或类(内容耦合),如果模块间必须存在耦合,原则上尽量使用数据耦合,少用控制耦合,同时限制公共耦合的范围,避免使用内容耦合。

最小化访问权限

  1. 类应该是不透明的,其内部的实现细节不应该暴露出来
  2. 从信息隐藏的角度来看,只要有可能就应该将方法和属性成员定义为私有的,并逐步增加访问权限。
  3. 定义专用数据修改方法的好处是可以对方法增加约束,比如验证输入等。
  4. 防御式编程和非防御式编程的区别在于开发者不对特定功能的调用和库的使用情况做想当然的预定。保护程序免受非法数据的破坏有3种常见的处理方法:
    (1) 检查来自于外部资源的数据值
    (2) 检查子程序所有输入参数的值
    (3) 决定如何处理错误的输入数据,对不同的错误类型进行处理。

以类代替基本的数据类型

类中如果有互相关联的多个基本数据类型,应该将其抽取成一个类(C#在这方面就实现了“完全的面向对象编程”—取消了基本数据类型)。

单一职责

单一职责原则就是一个类只设计用来实现一个职责,只会有一个引起变化的原因。面向对象设计和核心任务,就是发现职责并把这些职责相互分离;如果能够想到多于一个动机去修改一个类,那么这个类就具有多于一个职责,就应该考虑分解类的功能。
单一职责的优点有4个:
(1) 降低类的复杂性
(2) 提高可维护性
(3) 提高可读性
(4) 降低需求变化带来的风险。
再次补充:单一职责的“职责”不应以现实的物体来约束,而是应该以功能来进行辨识,例如:

public class Phone
{
        public void dial () {}
        public void hangup () {}
        public void send () {}
        public void receive () {}
}

这个类中的设计就违反了单一职责的原则,dail和hangup是电话的通信连接功能
而send和receive则是数据传送的功能,应修改为如下形式:

public interface IConnect
{
            public void dail ();
            public void hangup()
}
public interface ITransmit
{
    public void send ();
    public void receive ();
}
public class Phone implements Iconnect implements ITransmit{}

不要重复造轮子

如果需要多次用到一个硬编码值,那么将其设为公共常量(尽管这样可能会有风险);如果需要在两个以上的地方使用一个代码块,那么请重构代码,将它抽取为一个独立的方法(这是任何编程的常识)。

开放封闭原则(OCP)

即软件实体应该对扩展开放而对修改封闭。该原则主要体现在两个方面:
1.对扩展开放,意味着有新的需求变化时,可以对现有代码进行扩展,以适应新的情况
2.对修改封闭,意味着类一旦设计完成,就不要对类的实现做任何修改。
(我知道这样确实可以提高程序整体的安全性和可靠性,但维护的次数一多,这么庞大的代码冗余难道就这么放着不管了么?还是说要交给编译器处理?那岂不是比人工修改更加不可靠?)
实现OCP的核心思想就是对抽象编程,而不对具体编程,因为抽象相对稳定。让类依赖于固定的抽象,所以对修改就是封闭的;而通过继承和多态机制,可以实现对抽象体的具体化,通过重写其方法来改变固有行为,实现新的扩展方法,所以对于扩展就是开放的。
从编程的角度看,应强调针对接口编程,而不是针对实现编程。
以下是一个栗子(雾):

public class BankProcess
{
    public void deposite(){}
    public void withdraw(){}
    public void transfer(){}
}
public class BankStaff
{
    private BankProcess bankpro = new BankProcess();
    public void BankHandle (Client client)
    {
        switch (client.type)
        {
            case "deposite":
                bankpro.deposite();
                break;
            case "withdraw":
                bankpro.withdraw();
                break;
            case "transfer":
                bankpro.transfer();
                break;
        }
    }
}

(鉴于篇幅原因我就不讲究什么缩进了)
很明显,一旦以后在维护的过程中要添加银行的功能,势必要修改BankProcess类,这就违反了OCP原则。一个比较通用的解决方案是不使用类而使用借口来建立BankProess下的关系。如下:

public interface IBankProess
{
    void Proess();
}

public class DepositeProcess 
implements IBankProess
{
    public void Process ()
    {
        System.out.println("Process Deposite");
    }
}
public class WithDrawProcess
implements IBankProess
{
    public void Process ()
    {
        System.out.println("Process WithDraw");
    }
}
public class TransferProess 
implements IBankProess
{
    public void Process ()
    {
        System.out.println("Process Transfer");
    }
}

public class BankStaff
{
    private IBankProess bankpro = null;
    public void BankHandle (Client client)
    {
        switch (client.Type)
        {
            case "Deposite":
                bankpro = new DepositeProcess()
                break;
            case "Transer":
                bankpro = new TransferProess();
                break;
            case "WithDraw":
                bankpro = new WithDrawProcess();
                break;
        }
    }
}

在这里,尽管BankProcess类被分割,但新产生的三个类由于继承了相同的接口,因此彼此间存在隐性的联系,即原BankProcess。总的来说,换了一种方式来描述BankProcess,但新的方式具有更高的效率,即取消了“从属关系”的描述,将其转为隐式关系,使用“行为关系”描述,强化了扩展能力,在以后的维护的过程中不必再修改原有的内容,且符合OCP原则。

里氏替换原则(LSP)

只有当子类可以替换父类,软件单位的功能不受影响时,父类才能真正被复用,而子类也可以在父类的基础上增加新的行为。
继承必须确保父类中所拥有的性质在子类中仍然成立,也就是说子类必须能够替换成他的父类。(就是在确定子类的功能足够完善之前,不要使用向上转型)。

依赖倒置原则

程序要依赖于抽象,不要依赖于具体。简单说就是对抽象进行编程,不要对实现进行编程。


关于java8中新加入的特性Lambda表达式,在我看来就是提供了对函数式编程的支持,然而我很不能理解的是,为什么一个面向对象编程的语言要提供对函数式编程的支持。说实话,在我看法中,java的功能确实越来越多,但自身的却显得越来越臃肿,似乎有很多自相矛盾的功能被加入(比如一开始java主张的“用封装来保证安全性”,然而java又提供了反射的功能…….),不过这暂时还不是我需要关心的问题,所以只要说说就好了(躺~)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值