设计原则六大原则

219 阅读7分钟

1、单一职责

定义:一个类应该只有一个变化的原因。功能和职责单一
特点:降低类的复杂性,易于管理

反例:

interface IUser{
    String getName()
    String getAge()
    String getPassword()
    setName(String name)
    setAge(String age)
    setPassword(String password)
        
    changePassword()
    deleteUser()
}

这段代码中用户的属性和行为没有分开,不符合单一原则

改:

interface IUser{
    String getName()
    String getAge()
    String getPassword()
    setName(String name)
    setAge(String age)
    setPassword(String password)
}
    
interface IUserBiz{
    changePassword()
    deleteUser()
}

这样使用户的属性和行为进行区分。

2、里氏替换原则

定义:只要是父类可以出现的地方子类就可以出现
四个含义:
        a:子类必须完全实现父类的方法
        b:子类可以有自己的个性
        c:覆写或实现父类方法时,参数可以被“放大”
        d:覆写或者实现父类方法时,输出结(返回值)可以被缩小
使用注意事项:
    在开发中采用里氏替换原则时,尽量避免子类“个性”.
    一旦子类拥有“个性”时,把子类当父类用就抹杀了子类的个性;
    单独使用子类时会让代码耦合关系变得不明确,缺乏替换标准。

a:子类必须完全实现父类的方法

//接口类
public abstract class AbstractGun{
    public abstarct void shoot();
}
//实现类
public class HandGun extends AbstractGun{
    public void shoot(){
        System.out.println("手枪射击");
    }
}
    
public class Rifle extends AbstractGun{
    public void shoot(){
        System.out.println("步枪射击");
    }
}
    
public class MachineGun extends AbstractGun{
    public void shoot(){
        System.out.println("机枪射击");
    }
}
//士兵类
public class Soilder{
    private AbstractGun gun;
    public void setGun(AbstractGun gun){
        this.gun = gun;
    }
    public void killEnemy(){
        System.out.println("士兵拿出枪来啦!");
        gun.shoot();            
    }
}
//实际使用
public class client{
    public static void main(String[] args){
        Solider sanMao = new Solider();
        sanMao.setGun(new MachineGun());
        sanMao.shoot();
    }
}
当使用时,根据你传入的对象不同会做出不同的操作;
在编写时士兵不需要知道传入的是哪个Gun。只需要在使用时传入就可以了。

当子类不能完整的实现父类方法时建议断开关系,选择依赖、聚集、组合等关系代替

例:这是有个玩具枪

public class ToyGun extends AbstractGun{
    public void shoot(){
        //玩具枪是不具备射击功能的,所以这样继承并不合适
    }
}

玩具枪是不具备射击功能的,所以这样继承并不合适

//接口类
public abstract class AbstractToyGun extends AbstractGun{
    public abstarct void toyShoot();
}
public class ToyGun extends AbstractToyGun {
    public void toyShoot() {
        System.out.println("滋水枪");
    }
}

当按照上面设计时,这样更符合使用设计,形状和声音托管给真正枪,但是玩具枪不具备射击功能,所以士兵拿枪时候会报错

public class Client {
    public void main(String[] args){
        Solider sanMao = new Solider();
        sanMao.setGun(new ToyGun());//会报错
    }
}

b:子类可以有自己的个性

例:

//狙击枪
public AUG extends Rifle{
    public void zoomOut(){
        System.out.println("AUG瞄准");
    }
    public void shoot(){
        System.out.println("AUG射击");
    }
}
//狙击手
public class AUGSnipper extends Soilder{
    //狙击手射击依赖于狙击枪
    public void killEnemy(AUG aug){
        aug.zoomOut();
        aug.shoot();            
    }
}
//实际使用
public class client{
    public static void main(String[] args){
        Solider sanMao = new AUGSnipper();
        sanMao.setGun(new AUG());
        sanMao.shoot();
    }
}

***c:覆写或实现父类方法时,参数可以被“放大”(不能缩小)***
避免子类没有覆盖父类方法时,子类替换父类位置,子类方法却被执行的情况(方法重载情况)

***d:覆写或者实现父类方法时,输出结(返回值)可以被缩小(不能放大)***

3、依赖倒置原则

定义:更精简就是“面向接口编程”
    a:高层模块不该依赖底层模块,两者都应该依赖于抽象
    b:抽象不应该依赖于细节
    c:细节应该依赖于抽象

依赖倒置的意思其实就是依赖于实现类的时候,选择去依赖抽象,倒置依赖关系

优点:依赖倒置原装减少了类与类之间的强耦合
使用注意事项:
    每个类尽量都有接口类或者抽象类
    变量的表面类型尽量是接口过着抽象类
    任何类都不应该从具体类派生
    尽量不去覆写基类方法
    结合里氏替换原则使用
public interface IDriver{
    public void driver(ICar car);
}
    
public interface ICar{
    public void run();
}
    
public class BMW implements ICar{
    public void run(){
        System.out.println("宝马跑了~")
    }
}
        
public class Benz implements ICar{
    public void run(){
        System.out.println("奔驰跑了~")
    }
}
    
public class Client{
    public static void main(String[] args){
        IDriver sanMao = new Driver();
        ICar bmw = new BMW();
        sanMao.driver(bmw);
    }
}
    
//测试类
public class DriverTest extends TextClass{
    Mockery context = new JUni4Mockery();
    @Test
    public void testDriver(){
        final Icar car = context.mock(ICar.class);
         IDriver driver = new Driver();
         context.checking(new Expectation(){{
         oneOf(car).run();
         }});
         driver.driver(car);
    }
}

测试时只需要一个接口就能完成测试

三种写法:

1、构造函数传递依赖对象

public interface IDriver{
    public void driver()
}
    
public class MyDriver implements IDriver{
    private ICar car;
    public MyDriver(ICar _car){
        this.car = _car;
    }
    public void driver(){
        car.run();
    }
}

2、set方法传递依赖对象

public class MyDriver implements IDriver{
    private ICar car;
    public void setCar(ICar _car){
        this.car = _car;
    }
    public void driver(){
        car.run();
    }
}

3、在接口方法中声明依赖对象

public interface IDriver{
    public void driver(ICar car);
}
    
public class MyDriver implements IDriver{
    public void driver(ICar car){
        car.run();
    }
}

4、接口隔离原则

定义:客户端不应该依赖它不需要的接口;
    类的依赖关系应该建立在最小的接口上。

简单来说就是接口方法应该尽量少,尽量细化

原则: 
    接口内要高内聚(不依赖其他接口)
    定制服务,单独的为某一个功能而添加的接口,要查分
    接口的设计是有限度的,过多的接口会造成结构复杂,开发难度增大

反例:

//美女接口
public interface IPettyGirl{
    //好看
    public void goodlooking();
    //身材好
    public void niceShape();
    //气质好
    public void greatTemperament();
}
//美女在这里
public class PettyGirl implements IPettyGirl{
    String name;
    public PettyGirl(String _name){
        this.name = _name;
    }
    public void goodlooking(){
        System.out.println("好看的脸蛋");
    }
    public void niceShape(){
         System.out.println("好身材");
    }
    public void greatTemperament(){
         System.out.println("气质棒棒哒");
    }
}
//找美女抽象
public abstract class AbstractSearcher{
    provited IPettyGirl pettyGirl;
    public AbstractSrarch(IPettyGirl _pettyGirl){
        this.pettyGirl = _pettyGirl;
    }
    public void show();
}
//找美女
public Searcher entends AbstractSearcher{
    public Searcher(IPettyGirl _pettyGirl){
        super_pettyGirl);
    }
    public void show(){
        super.pettyGirl.goodlooking();
        super.pettyGirl.niceShape();
        super.pettyGirl.greatTemperament();
    }
}

这段代码中定义了美女的类型,但是有一点就是现在的美女定义有区别(气质美女,和非气质美女),这样就造成了接口过大,所以这个接口是应该拆分的

//美女接口
public interface IGoodBodyGirl{
    //好看
    public void goodlooking();
    //身材好
    public void niceShape();
}
public interface IGreatTemperamentGirl{
    //气质好
    public void greatTemperament();
}
//美女在这里
public class PettyGirl implements IPettyGirl,IGreatTemperamentGirl{
    String name;
    public PettyGirl(String _name){
        this.name = _name;
    }
    public void goodlooking(){
        System.out.println("好看的脸蛋");
    }
    public void niceShape(){
         System.out.println("好身材");
    }
    public void greatTemperament(){
         System.out.println("气质棒棒哒");
    }
}

这样在定义美女的时候就可以分别定义气质美女,和非气质美女了,而不会造成接口的修改

5、迪米特法则

定义:只与直接耦合关系的类进行通信

限制了类与类之间的耦合关系,要求类间解耦、弱耦合,提高复用率减少系统复杂程度

反例:

//老师类
public class Teacher{
    //老师指挥课代表清点人数
    public void commond(GroupLeader groupLeader){
    List listGirls = new ArrayList();
    for(int i = 0; i<20;i++){
        listGirls.add(new Girl());
    }
    groupLeader.countGirls();
    }
}

//女生类(只需要一个空类)
public class Girl{
    
}

//课代表
public class GroupLeader{
    public void countGirls(List<Girl> listGirls){
        System.out.println("女生的人数===>"+listGirls.size())
    }
}

//使用
public class Client{
    public voic main(String[] args){
        Teacher teacher = new Teacher();
        teacher.commond();
    }
}

这个例子中teacher类中耦合的女生类明显是不必要的

public class Teacher{
    public void commond(GroupLeader groupleader){
        groupLeader.countGirls();
    }
}

public class GroupLeader{
    private List<Girl> listGirls;
    public GroupLeader(List<Girl> _listGirls){
        this.listGirls = _listGirls;
    }
    public void countGirls(){
        System.out.println("女孩儿的人数为===>"+listGirls.size());
    }
}

尽量减少public向外暴露,尽量的高内聚代码不依赖其他类

反例

//安装类
public class Wizard{
    private Random random = new Random(System.ourrentTimeMillis());
    
    public int first(){
        System.out.println("第一步");
        return random.nextInt(100);
    }    
    public int second(){
        System.out.println("第二步");
        return random.nextInt(100);
    }    
    public int third(){
        System.out.println("第三步");
        return random.nextInt(100);
    }
}
//安装导向类
public class Insrall{
    public void installWizard(Wizard wizard){
        int first = wizard.first();
        if(first>50){
            int second = wizard.second();
            if(second>50){
                int third = wizard.third();
                if(third>50){
                   System.out.println("安装完成");
                }
            }
        }
    }
}
//使用
public class Client{
    public void main(String[] args){
        Install install = new Install();
        install.installWizard(new Wizard());
    }
}

public class InstallWizard{
    private Random random = new Random(System.ourrentTimeMillis());
    
    private int first(){
        System.out.println("第一步");
        return random.nextInt(100);
    }    
    private int second(){
        System.out.println("第二步");
        return random.nextInt(100);
    }    
    private int third(){
        System.out.println("第三步");
        return random.nextInt(100);
    }
    public void installWizard(){
        this.first();
        if(first>50){
            this.second();
            if(second>50){
                this.third();
                if(third>50){
                   System.out.println("安装完成");
                }
            }
        }
    }
}

原则:如果一个方法放在本类既不增加类间关系,也不会对本类产生负面影响,则放置在本类中

谨慎使用 Serializable 修改容易导致序列化失败问题

6、开闭原则

等我足够的理解了再写

我一直都知道是对扩展开放对修改关闭,但是貌似只是个口号,从来都不知道如何实现

实现方案:

1、抽象约束

2、元数据控制模块行为

元数据指的是基础数据,配置参数、环境等的数据

3、制定项目章程

4、封装变化