大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

專注Java教育14年 全國咨詢/投訴熱線:400-8080-105
動(dòng)力節(jié)點(diǎn)LOGO圖
始于2009,口口相傳的Java黃埔軍校
首頁 hot資訊 6大設(shè)計(jì)原則

6大設(shè)計(jì)原則

更新時(shí)間:2021-10-14 09:50:14 來源:動(dòng)力節(jié)點(diǎn) 瀏覽697次

1.設(shè)計(jì)模式的目的

設(shè)計(jì)模式是為了更好的代碼重用性,可讀性,可靠性,可維護(hù)性。

2.常用的六大設(shè)計(jì)原則

(1)單一職責(zé)原則

(2)里氏替換原則

(3)依賴倒轉(zhuǎn)原則

(4)接口隔離原則

(5)迪米特法則

(6)開閉原則

3.單一職責(zé)原則

該原則是針對(duì)類來說的,即一個(gè)類應(yīng)該只負(fù)責(zé)一項(xiàng)職責(zé)。

如類T負(fù)責(zé)兩個(gè)不同職責(zé):職責(zé)P1,職責(zé)P2。當(dāng)職責(zé)P1需求變更而改變T時(shí),可能造成職責(zé)P2發(fā)生故障,所以需要將類T的粒度分解為T1,T2。

示例如下:

用一個(gè)類秒數(shù)動(dòng)物呼吸這個(gè)場(chǎng)景

class Animal {
    public void breathe(string animal)
    {
        Console.WriteLine(animal+"呼吸空氣");
    }
}
class Program
{
    static void Main(string[] args)
    {
        Animal animal = new Animal();
        animal.breathe("牛");
        animal.breathe("羊");
        animal.breathe("豬");
        animal.breathe("魚");
        Console.ReadLine();
    }
}

輸出結(jié)果:

我們發(fā)現(xiàn)不是所有動(dòng)物都是呼吸空氣的,比如魚就是呼吸水的,根據(jù)單一職責(zé)原則,我們將Animal類細(xì)分為陸生動(dòng)物類和水生動(dòng)物類,如下所示:

class Terrestrial
{
    public void breathe(string animal)
    {
        Console.WriteLine(animal+"呼吸空氣");
    }
}
class Aquatic
{
    public void breathe(string animal)
    {
        Console.WriteLine(animal + "呼吸水");
    }
}
class Program
{
    static void Main(string[] args)
    {
        Terrestrial terrestrial = new Terrestrial();
        terrestrial.breathe("牛");
        terrestrial.breathe("羊");
        terrestrial.breathe("豬");
        Aquatic aquatic = new Aquatic();
        aquatic.breathe("魚");
        Console.ReadLine();
    }
}

我們發(fā)現(xiàn)這樣修改的花銷很大,既要將原來的類分解,又要修改客戶端。而直接修改Animal類雖然違背了單一職責(zé)原則,但花銷小的多,如下所示:

class Animal
{
    public void breathe(string animal)
    {
        if ("魚".Equals(animal))
        {
            Console.WriteLine(animal + "呼吸水");
        }
        else {
            Console.WriteLine(animal + "呼吸空氣");
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        Animal animal = new Animal();
        animal.breathe("牛");
        animal.breathe("羊");
        animal.breathe("豬");
        animal.breathe("魚");
        Console.ReadLine();
    }
}

可以看到,這種修改方式簡單的多。但卻存在隱患,一天需要將魚分為淡水魚,海水魚,又需要修改Animal類的breathe方法。可能給“豬牛羊”等相關(guān)功能帶來風(fēng)險(xiǎn),這種修改直接在代碼級(jí)別違背了單一職責(zé)原則,雖然修改起來最簡單,但隱患最大。還有一種修改方式:

class Animal
{
    public void breathe(string animal)
    {
         Console.WriteLine(animal + "呼吸空氣");
    }
    public void breathe2(string animal)
    {
        Console.WriteLine(animal + "呼吸水");
    }
}
class Program
{
    static void Main(string[] args)
    {
        Animal animal = new Animal();
        animal.breathe("牛");
        animal.breathe("羊");
        animal.breathe("豬");
        animal.breathe2("魚");
        Console.ReadLine();
    }
}

這種修改方式?jīng)]有改動(dòng)原來的方法,而是在類中新加了一個(gè)方法,這樣雖然違背了單一職責(zé)原則,但在方法級(jí)別上卻是符合單一職責(zé)原則的。

4.里氏替換原則

該原則是在1988年,由麻省理工學(xué)院的以為姓里的女士提出的。

如果對(duì)每個(gè)類型為T1的對(duì)象o1,都有類型為T2的對(duì)象o2,使得以T1定義的所有程序P在所有的對(duì)象o1都代換成o2時(shí),程序P的行為沒有發(fā)生變化,那么類型T2是類型T1的子類型。

換句話說,所有引用基類的地方必須能透明地使用其子類的對(duì)象。

由定義可知,在使用繼承時(shí),遵循里氏替換原則,在子類中盡量不要重寫和重載父類的方法。

繼承包含這樣一層含義:父類中凡是已經(jīng)實(shí)現(xiàn)好的方法(相對(duì)抽象方法而言),實(shí)際上是在設(shè)定一系列的規(guī)范和契約,雖然它不強(qiáng)制要求所有的子類必須遵循這些契約,但是如果子類對(duì)這些非抽象方法任意修改,就會(huì)對(duì)整個(gè)繼承體系造成破壞。而里氏替換原則就是表達(dá)了這一層含義。

繼承作為面向?qū)ο笕筇匦灾唬诮o程序設(shè)計(jì)帶來巨大遍歷的同時(shí),也帶來了弊端。比如使用繼承會(huì)給程序帶來侵入性,程序的可移植性降低,增加對(duì)象間的耦合性,如果一個(gè)類被其他的類所繼承,則當(dāng)這個(gè)類需要修改時(shí),必須考慮到所有的子類,并且父類修改后,所有涉及到子類的功能都有可能產(chǎn)生故障。

舉例說明繼承的風(fēng)險(xiǎn),我們需要完成一個(gè)兩數(shù)相減的功能,由類A來負(fù)責(zé)。

class A{
    public int func1(int a,int b){
        return a-b;
    }
}
public class Client{
    public static void main(string[] args){
        A a=new A();
        System.out.println("100-50="+a.func1(100,50));
        System.out.println("100-80="+a.func1(100,80));
    }
}

運(yùn)行結(jié)果:

100-50=50

100-80=20

后來,我們需要增加一個(gè)新的功能:完成兩數(shù)相加,然后再與100求和,由類B來負(fù)責(zé)。

Class B extends A{
    public int func1(int a,int b){
        return a+b;
    }
    public int func2(int a,int b){
        return func1(a,b)+100;
    }
}
public class Client{
    public static void main(string[] args){
        B a=new B();
        System.out.println("100-50="+b.func1(100,50));
        System.out.println("100-80="+b.func1(100,80));
        System.out.println("100+20+100="+b.func2(100,20));
    }
}

運(yùn)行結(jié)果:

100-50=150

100-80=180

100+20+100=220

我們發(fā)現(xiàn)原來運(yùn)行正常的相減功能發(fā)生了錯(cuò)誤。原因就是類B無意中重寫了父類的方法,造成原有功能出現(xiàn)錯(cuò)誤。在實(shí)際編程中,我們常常會(huì)通過重寫父類的方法完成新的功能,這樣寫起來雖然簡單,但整個(gè)繼承體系的復(fù)用性會(huì)比較差。特別是運(yùn)行多態(tài)比較頻繁的時(shí)候,如果非要重寫父類的方法,通用的做法是:原來的父類和子類都繼承一個(gè)更通俗的基類,原有的繼承關(guān)系去掉,采用依賴,聚合,組合等關(guān)系代替。

5.依賴倒轉(zhuǎn)原則

高層模塊不應(yīng)該依賴低層模塊,二者都應(yīng)該依賴其抽象;抽象不應(yīng)該依賴細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴抽象。

類A直接依賴類B,如果要將類A改為依賴類C,則必須通過修改類A的代碼來達(dá)成。此時(shí),類A一般是高層模塊,負(fù)責(zé)復(fù)雜的業(yè)務(wù)邏輯,類B和類C是低層模塊,負(fù)責(zé)基本的原子操作;修改A會(huì)給程序帶來風(fēng)險(xiǎn)。

將類A修改未依賴接口I,類B和類C各自實(shí)現(xiàn)接口I,類A通過接口I間接與類B或類C發(fā)生聯(lián)系,則會(huì)大大降低修改類A的記幾率。

依賴倒置原則基于這樣一個(gè)事實(shí):相對(duì)于細(xì)節(jié)的多變性,抽象的東西要穩(wěn)定的多。以抽象為基礎(chǔ)搭建的架構(gòu)比以細(xì)節(jié)為基礎(chǔ)的架構(gòu)要穩(wěn)定的多。在java中,抽象指的是接口或抽象類,細(xì)節(jié)就是具體的實(shí)現(xiàn)類,使用接口或抽象類的目的是制定好規(guī)范,而不涉及任何具體的操作,把展現(xiàn)細(xì)節(jié)的任務(wù)交給他們的實(shí)現(xiàn)類去完成。

依賴倒置的中心思想是面向接口編程。

代碼示例如下:

class Book {
    public string getContent() {
        return "很久很久以前。。。。。";
    }
}
class Mother {
    public void narrate(Book book)
    {
        Console.WriteLine(book.getContent());
    }
}
class Program
{
    static void Main(string[] args)
    {
        Mother monther = new Mother();
        monther.narrate(new Book());
        Console.ReadLine();
    }
}

運(yùn)行結(jié)果:

如果讀的對(duì)象是報(bào)紙,雜志,卻發(fā)現(xiàn)客戶端不適用了。

我們引入一個(gè)抽象的接口IReader,代表讀物

interface IReader{
    public string getContent();
}

這樣Mother類與接口IReader發(fā)生依賴關(guān)系,而Book和Newspaper都屬于讀物的范疇,他們各自都去實(shí)現(xiàn)IReader接口,這樣就符合依賴倒置原則了,修改代碼如下:

interface IReader {
         string getContent();
    }
    class Newspaper: IReader
    {
    public string getContent()
    {
        return "切爾西豪取12連勝";
    }
}
class Book:IReader
{

    public string getContent()
{
    return "很久很久以前。。。。";
}
}
class Mother
{
    public void narrate(IReader reader)
    {
        Console.WriteLine(reader.getContent());
    }
}
class Program
{
    static void Main(string[] args)
    {
        Mother monther = new Mother();
        monther.narrate(new Book());
        monther.narrate(new Newspaper());
        Console.ReadLine();
    }
}

運(yùn)行結(jié)果:

采用依賴倒置原則給多人并行開發(fā)帶來極大的便利,比如上列中Mother類與Book類直接耦合,Mother必須等Book類編碼完成后才可以進(jìn)行編碼,因?yàn)镸other類依賴于Book類。修改后的程序可以同時(shí)開工,互不影響。

依賴關(guān)系的傳遞有三種方式,接口傳遞,構(gòu)造方法傳遞和setter方法傳遞。

接口傳遞:

interface IDriver{
    public void drive(ICar car);
}
public class Driver:IDriver{
    public void drive(ICar car){
        car.run();
    }
}

構(gòu)造方法傳遞:

interface IDriver{
    public void drive();
}
public class Driver implements IDriver{
    public ICar car;
    public Driver(ICar _car){
        this.car=_car;
    }
    public void drive(){
        this.car.run();
    }
}

setter方式傳遞:

interface IDriver{
    public void setCar(ICar car);
    public void drive();
}
public class Driver:IDriver{
    PRIVATE ICar car;
    public void setCar(ICar car){
        this.car=car;
    }
    public void drive(){
        this.car.run();
    }
}

在實(shí)際編程中,一般需要做到如下3點(diǎn):

低層模塊盡量都要有抽象類或接口,或者兩者都有。

變量的聲明類型盡量是抽象類或接口。

使用繼承時(shí)遵循里氏替換原則

6.接口隔離原則

客戶端不應(yīng)該依賴它不需要的接口;一個(gè)類對(duì)另一個(gè)類的依賴應(yīng)該建立在最小的接口上。

類A通過接口I依賴類B,類C通過接口I依賴類D,如果接口I對(duì)于類A和類C來說不是最小接口,則類B和類D必須去實(shí)現(xiàn)他們不需要的方法。

將臃腫的接口I拆分為獨(dú)立的幾個(gè)接口,類A和類C分別與他們需要的接口建立依賴關(guān)系。也就是采用接口隔離原則。

舉例說明接口隔離原則:

這個(gè)圖的意思是:類A依賴接口I中的方法1,方法2,方法3,類B是對(duì)類A依賴的實(shí)現(xiàn);類C依賴接口I中的方法1,方法4,方法5,類D是對(duì)類C依賴的實(shí)現(xiàn)。對(duì)于類B和類D來說,雖然存在用不到的方法(紅色標(biāo)記所示),但由于實(shí)現(xiàn)了接口I,所以也必須要實(shí)現(xiàn)這些用不到的方法。代碼如下:

interface I{
    void method1();
    void method2();
    void method3();
    void method4();
    void method5();
}
class A{
    public void depend1(I i){
        i.method1();
    }
    public void depend2(I i){
        i.method2();
    }
    public void depend3(I i){
        i.method3();
    }
}
class C{
    public void depend1(I i){
        i.method1();
    }
    public void depend2(I i){
        i.method4();
    }
    public void depend3(I i){
        i.method5();
    }
}
class B:I{
    public void method1(){
        Console.WriteLine("類B實(shí)現(xiàn)接口I的方法1");
    }
    public void method2(){
        Console.WriteLine("類B實(shí)現(xiàn)接口I的方法2");
    }
    public void method3(){
        Console.WriteLine("類B實(shí)現(xiàn)接口I的方法3");
    }
    public void method4(){}
    public void method5(){}
}
class D:I{
    public void method1(){
        Console.WriteLine("類B實(shí)現(xiàn)接口I的方法1");
    }
    public void method2(){}
    public void method3(){}
    public void method4(){
        Console.WriteLine("類B實(shí)現(xiàn)接口I的方法4");
    }
    public void method5(){
        Console.WriteLine("類B實(shí)現(xiàn)接口I的方法5");
    }
}
class Program
{
    static void Main(string[] args)
    {
        A a=new A();
        a.depend1(new B());
        a.depend2(new B());
        a.depend3(new B());
        
        C c=new C();
        c.depend1(new D());
        c.depend2(new D());
        c.depend3(new D());
        Console.ReadLine();
    }
}

可以看到,接口中出現(xiàn)的方法,不管對(duì)依賴于它的類有沒有作用,實(shí)現(xiàn)類中都必須去實(shí)現(xiàn)這些方法。于是我們將原接口I拆分為三個(gè)接口:

代碼如下所示:

interface I1{
    void method1();
}
interface I2{
    void method2();
    void method3();
}
interface I3{
    void method4();
    void method5();
}
class A{
    public void depend1(I1 i){
        i.method1();
    }
    public void depend2(I2 i){
        i.method2();
    }
    public void depend3(I2 i){
        i.method3();
    }
}
class C{
    public void depend1(I1 i){
        i.method1();
    }
    public void depend2(I3 i){
        i.method4();
    }
    public void depend3(I3 i){
        i.method5();
    }
}
class B:I1,I2{
    public void method1(){
        Console.WriteLine("類B實(shí)現(xiàn)接口I1的方法1");
    }
    public void method2(){
        Console.WriteLine("類B實(shí)現(xiàn)接口I2的方法2");
    }
    public void method3(){
        Console.WriteLine("類B實(shí)現(xiàn)接口I2的方法3");
    }
}
class D:I1,I3{
    public void method1(){
        Console.WriteLine("類B實(shí)現(xiàn)接口I的方法1");
    }
    public void method4(){
        Console.WriteLine("類B實(shí)現(xiàn)接口I的方法4");
    }
    public void method5(){
        Console.WriteLine("類B實(shí)現(xiàn)接口I的方法5");
    }
}
class Program
{
    static void Main(string[] args)
    {
        A a=new A();
        a.depend1(new B());
        a.depend2(new B());
        a.depend3(new B());
        
        C c=new C();
        c.depend1(new D());
        c.depend2(new D());
        c.depend3(new D());
        Console.ReadLine();
    }
}

說到這里,可能會(huì)覺得接口隔離原則和之前的單一職責(zé)原則很相似,其實(shí)不然。一,單一職責(zé)注重職責(zé),而接口隔離原則注重對(duì)接口依賴的隔離;二,單一職責(zé)是約束類,其次是方法,針對(duì)的是程序中的實(shí)現(xiàn)和細(xì)節(jié);而接口隔離原則約束的是接口,針對(duì)的是抽象,程序整體框架的構(gòu)建。

7.迪米特法則

一個(gè)對(duì)象應(yīng)該對(duì)其他對(duì)象保持最少的了解。

類與類關(guān)系越密切,耦合度越大。

迪米特法則又叫最少知道原則,即一個(gè)類對(duì)自己依賴的類知道的越少越好。也就是說,對(duì)于被依賴的類不管多么復(fù)雜,都盡量將邏輯封裝在類的內(nèi)部。對(duì)外除了提供的public 方法,不對(duì)外泄露任何信息。

迪米特法則還有個(gè)更簡單的定義:只與直接的朋友通信。

什么是直接的朋友:每個(gè)對(duì)象都會(huì)與其他對(duì)象由耦合關(guān)系,只要兩個(gè)對(duì)象之間有耦合關(guān)系,我們就說這兩個(gè)對(duì)象之間是朋友關(guān)系。耦合的方式很多,依賴,關(guān)聯(lián),組合,聚合等。其中,我們稱出現(xiàn)成員變量,方法參數(shù),方法返回值中的類為直接的朋友,而出現(xiàn)在局部變量中的類不是直接的朋友。也就是說,陌生的類最好不要以局部變量的形式出現(xiàn)在類的內(nèi)部。

舉例額說明如下,有一個(gè)集團(tuán)公司,下屬單位有分公司和直屬部門,現(xiàn)要求打印出所有下屬單位的員工ID。

class Employee{
    private string id;
    public void setId(string id){
        this.id=id;
    }
    public string getId(){
        return id;
    }
}
class SubEmployee{
    private string id;
    public void setId(string id){
        this.id=id;
    }
    public string getId(){
        return id;
    }
}
class SubCompanyManager{
    public List<SubEmployee> getAllEmployee(){
        List<SubEmployee> list=new ArrayList(SubEmployee);
        for(int i=0;i<100;i++){
            SubEmployee emp=new SubEmployee();
            emp.setId("分公司"+i);
            list.add(emp);
        }
        return list;
    }
}
class CompanyManager{
    public List<Employee> getAllEmployee(){
        List<Employee> list=new ArrayList<Employee>();
        for(int i=0;i<30;i++)
        {
            Employee emp=new Employee();
            emp.setId("總公司"+i);
            list.add(emp);
        }
        return list;
    }
    publi void printAllEmployee(SubCompanyManager sub){
        List<SubEmployee> list1=sub.getAllEmployee();
        foreach(SubEmployee e in list1){
            Console.WriteLine(e.getId());
        }
        List<Employee> list2=this.getAllEmployee();
        foreach(Employee e in list2){
            Console.WriteLine(e.getId());
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        CompanyManager e=new CompanyManager();
        e.printAllEmployee(new SubCompanyManager());
        Console.ReadLine();
    }
}

這個(gè)設(shè)計(jì)的問題在于CompanyManager中,SubEmployee類并不是CompanyManager類的直接朋友,按照迪米特法則,應(yīng)該避免類中出現(xiàn)這樣非直接朋友關(guān)系的耦合。修改后的代碼如下:

class SubCompanyManager{
    public List<SubEmployee> getAllEmployee(){
        List<SubEmployee> list = new ArrayList<SubEmployee>();
        for(int i=0; i<100; i++){
            SubEmployee emp = new SubEmployee();
            //為分公司人員按順序分配一個(gè)ID
            emp.setId("分公司"+i);
            list.add(emp);
        }
        return list;
    }
    public void printEmployee(){
        List<SubEmployee> list = this.getAllEmployee();
        for(SubEmployee e:list){
            System.out.println(e.getId());
        }
    }
}
class CompanyManager{
    public List<Employee> getAllEmployee(){
        List<Employee> list = new ArrayList<Employee>();
        for(int i=0; i<30; i++){
            Employee emp = new Employee();
            //為總公司人員按順序分配一個(gè)ID
            emp.setId("總公司"+i);
            list.add(emp);
        }
        return list;
    }    
    public void printAllEmployee(SubCompanyManager sub){
        sub.printEmployee();
        List<Employee> list2 = this.getAllEmployee();
        for(Employee e:list2){
            System.out.println(e.getId());
        }
    }
}

迪米特法則的初衷是降低類之間的耦合,由于每個(gè)類都減少了不必要的依賴,因此的確可以降低耦合關(guān)系。

8.開閉原則

一個(gè)軟件實(shí)體如類,模塊和函數(shù)應(yīng)該對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉。用抽象構(gòu)建框架,用實(shí)現(xiàn)擴(kuò)展細(xì)節(jié)。

當(dāng)軟件需要變化時(shí),盡量通過擴(kuò)展軟件實(shí)體的行為來實(shí)現(xiàn)變化,而不是通過修改已有的代碼來實(shí)現(xiàn)變化。

當(dāng)我們遵循前面介紹的5大原則,以及使用23中設(shè)計(jì)模式的目的就是遵循開閉原則。

以上就是關(guān)于“6大設(shè)計(jì)原則”的介紹,如果大家想了解更多相關(guān)信息,可以關(guān)注一下動(dòng)力節(jié)點(diǎn)的面向?qū)ο笤O(shè)計(jì)原則,里面有更多的知識(shí)在等待著大家,希望對(duì)大家能夠有所幫助。

提交申請(qǐng)后,顧問老師會(huì)電話與您溝通安排學(xué)習(xí)

免費(fèi)課程推薦 >>
技術(shù)文檔推薦 >>
主站蜘蛛池模板: 自拍亚洲国产 | 亚洲精品tv久久久久久久久久 | 四色婷婷| 国产一区亚洲二区三区 | 国产精品福利一区二区 | 午夜影院普通 | 狼人伊人干 | 日韩在线第二页 | 国产精品视频麻豆 | 99在线观看免费视频 | 四虎视频网站 | 久久精品操 | 99久久日本一区二区波多野结衣 | 亚洲国产精品第一区二区 | 亚洲国产精品久久综合 | 国产99免费视频 | 日韩欧美亚洲国产精品字幕久久久 | 日本a中文字幕 | 日韩一级欧美一级毛片在线 | 亚洲在线视频 | 加勒比一本大道在线 | 亚洲性一区| 精品国产一区二区三区香蕉事 | 国产亚洲精品美女久久久 | 玖玖影院在线观看 | 亚洲日本视频在线 | 四虎永久在线日韩精品观看 | 四虎影视永久免费观看地址 | 婷婷国产天堂久久综合五月 | 日本在线中文 | 91手机在线| 免费观看国产精品视频 | 91久久国产综合精品女同国语 | 美女一级a毛片免费观看 | 精品欧美在线 | 日本色图在线 | 中国美女一级a毛片录像在线 | 久久精品国产69国产精品亚洲 | 中文字幕亚洲国产 | 中文字幕久久亚洲一区 | 亚洲一区二区三区在线网站 |