前端开发

2020-04-24design princeple

2020-05-26  本文已影响0人  itsmyturn

一,什么是设计

二,SOLID 五大设计原则

首字母 指代 概念
S 单一职责原则 单一功能原则认为对象应该仅具有单一功能 的概念
O 开放封闭原则 开闭原则认为 软件体应该是对外开放的,但是对于修改封闭的概念
L 里式替换原则 里式替换原则认为程序中的对象应该是可以在不改变程序正确性的前提下 被它的子类替换 的概念
I 接口隔离原则 接口隔离原则认为 多个特定的客户端接口要好于一个宽泛用途的接口 的概念
D 依赖反转原则 依赖反转原则认为一个方法应该遵循 依赖于抽象而不是一个实例 的概念 依赖注入是该原则实现的一种方式

1.O开放封闭原则

不好的设计

class Product{
  constructor(name,price){
    this.name=name
    this.price=price
  }
  cost(customer){
    switch(customer.rank){
      case 'member':
          return this.price*.8
      case 'vip':
          return this.price*.6
      default:
        return this.price
    }
  }
}
class Customer{
  constructor(rank){
    this.rank=rank
  }
}
let product=new Product('笔记本电脑',1000)
let member=new Customer('member')
let vip=new Customer('vip')
let guest=new Customer('guest')
console.log(product.cost(member))
console.log(product.cost(vip))
console.log(product.cost(guest))

好的设计

class Product{
  constructor(name,price){
    this.name=name
    this.price=price
  }
  cost(customer){
    return this.price*customer.discount
  }
}
class Customer{
  constructor(rank,discount=1){
    this.rank=rank
    this.discount=discount
  }
}
let product=new Product('笔记本电脑',1000)
let member=new Customer('member',.8)
let vip=new Customer('vip',.6)
let guest=new Customer('guest')
let superVip=new Customer('superVip',.4)
console.log(product.cost(member))
console.log(product.cost(vip))
console.log(product.cost(guest))
console.log(product.cost(superVip))



2. S 单一职责原则

3. L里氏替换原则

好的设计

class Drink{
  //抽象类abstract
  getPrice(){}
}
class CocaCola extends Drink{
  getPrice(){
    return 3
  }
}
class Sprite extends Drink{
  getPrice(){
    return 3
  }
}
class Fanta extends Drink{
  getPrice(){
    return 5
  }
}

class Customer{
  drink(AbstrackDrink){
    console.log(`花费${AbstrackDrink.getPrice()}`)
  }
}
let c1=new Customer()
c1.drink(new CocaCola())

不好的设计

class Drink{
  //抽象类abstract
  getPrice(){}
}
class CocaCola extends Drink{
  getPrice(){
    return '我是一瓶可口可乐' //子类违反了父类的功能和规定
  }
}
class Sprite extends Drink{
  getPrice(){
    return 3
  }
}
class Fanta extends Drink{
  getPrice(){
    return 5
  }
}

class Customer{
  drink(AbstrackDrink){
    console.log(`花费${AbstrackDrink.getPrice()}`)
  }
}
let c1=new Customer()
c1.drink(new CocaCola())

4.D 依赖倒置原则

依赖倒置原则的主要作用如下:
  1. 依赖倒置原则可以降低类间的耦合性。
  2. 依赖倒置原则可以提高系统的稳定性。
  3. 依赖倒置原则可以减少并行开发引起的风险。
  4. 依赖倒置原则可以提高代码的可读性和可维护性
依赖倒置原则的实现方法

依赖倒置原则的目的是通过要面向接口的编程来降低类间的耦合性,所以我们在实际编程中只要遵循以下4点,就能在项目中满足这个规则。

  1. 每个类尽量提供接口或抽象类,或者两者都具备。
  2. 变量的声明类型尽量是接口或者是抽象类。
  3. 任何类都不应该从具体类派生。
  4. 使用继承时尽量遵循里氏替换原则
    依赖倒置描述链接
购物图例
package principle;
public class DIPtest
{
    public static void main(String[] args)
    {
        Customer wang=new Customer();
        System.out.println("顾客购买以下商品:"); 
        wang.shopping(new ShaoguanShop()); 
        wang.shopping(new WuyuanShop());
    }
}
//商店
interface Shop
{
    public String sell(); //卖
}
//韶关网店
class ShaoguanShop implements Shop
{
    public String sell()
    {
        return "韶关土特产:香菇、木耳……"; 
    } 
}
//婺源网店
class WuyuanShop implements Shop
{
    public String sell()
    {
        return "婺源土特产:绿茶、酒糟鱼……"; 
    }
} 
//顾客
class Customer
{
    public void shopping(Shop shop)
    {
        //购物
        System.out.println(shop.sell()); 
    }
}
依赖倒置原则
interface Girlfriend{
  age:number
  height:number
  cook():void
}
class LinChiling implements Girlfriend{
  age:number=35
  height:number=178
  cook(){
    console.log('泡面')
  }
}
class HanMeimei implements Girlfriend{
  age:number=35
  height:number=178
  cook(){
    console.log('泡面')
  }
}
class SingleDog{
  constructor(public girlfriend:Girlfriend){

  }
}
let dog1=new SingleDog(new LinChiling())
let dog2=new SingleDog(new HanMeimei())

5 接口隔离原则

interface Runing{
  run():void
}
interface Flying{
  fly():void
}
interface Swimming{
  swim():void
}
class Automobile implements Runing,Flying,Swimming{
  run(){}
  fly(){}
  swim(){}
}

6 迪米特法则

迪米特法则的优点

  1. 迪米特法则要求限制软件实体之间通信的宽度和深度,正确使用迪米特法则将有以下两个优点。
  2. 降低了类之间的耦合度,提高了模块的相对独立性。
  3. 由于亲合度降低,从而提高了类的可复用率和系统的扩展性。

缺点

但是,过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低。所以,在釆用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。

迪米特法则的实现方法

从迪米特法则的定义和特点可知,它强调以下两点:
1.从依赖者的角度来说,只依赖应该依赖的对象。

  1. 从被依赖者的角度说,只暴露应该暴露的方法。

所以,在运用迪米特法则时要注意以下 6 点。

  1. 在类的划分上,应该创建弱耦合的类。类与类之间的耦合越弱,就越有利于实现可复用的目标。
  2. 在类的结构设计上,尽量降低类成员的访问权限。
  3. 在类的设计上,优先考虑将一个类设置成不变类。
  4. 在对其他类的引用上,将引用其他对象的次数降到最低。
  5. 不暴露类的属性成员,而应该提供相应的访问器(set 和 get 方法)。
  6. 谨慎使用序列化(Serializable)功能。

示例1

class Salesman{
  constructor(public name:string){

  }
  sale(){
    console.log(this.name+'销售中。。。。。。。。。。')
  }
}
class SaleManager{
  public salesmen:Array<Salesman>=[new Salesman('张三'),new Salesman('李四')]
  sale(){
    this.salesmen.forEach(salesman=>salesman.sale())
  }
}
class CEO{
  private saleManager:SaleManager=new SaleManager()
  sale(){
    this.saleManager.sale()
  }
}
let ceo=new CEO()
ceo.sale()

示例2 明星与经纪人的关系实例

分析:明星由于全身心投入艺术,所以许多日常事务由经纪人负责处理,如与粉丝的见面会,与媒体公司的业务洽淡等。这里的经纪人是明星的朋友,而粉丝和媒体公司是陌生人,所以适合使用迪米特法则


image.png
package principle;
public class LoDtest
{
    public static void main(String[] args)
    {
        Agent agent=new Agent();
        agent.setStar(new Star("林心如"));
        agent.setFans(new Fans("粉丝韩丞"));
        agent.setCompany(new Company("中国传媒有限公司"));
        agent.meeting();
        agent.business();
    }
}
//经纪人
class Agent
{
    private Star myStar;
    private Fans myFans;
    private Company myCompany;
    public void setStar(Star myStar)
    {
        this.myStar=myStar;
    }
    public void setFans(Fans myFans)
    {
        this.myFans=myFans;
    }
    public void setCompany(Company myCompany)
    {
        this.myCompany=myCompany;
    }
    public void meeting()
    {
        System.out.println(myFans.getName()+"与明星"+myStar.getName()+"见面了。");
    }
    public void business()
    {
        System.out.println(myCompany.getName()+"与明星"+myStar.getName()+"洽淡业务。");
    }
}
//明星
class Star
{
    private String name;
    Star(String name)
    {
        this.name=name;
    }
    public String getName()
    {
        return name;
    }
}
//粉丝
class Fans
{
    private String name;
    Fans(String name)
    {
        this.name=name;
    }
    public String getName()
    {
        return name;
    }
}
//媒体公司
class Company
{
    private String name;
    Company(String name)
    {
        this.name=name;
    }
    public String getName()
    {
        return name;
    }
}

7.合成复用原则

1.类的关系

合成复用原则的重要性

通常类的复用分为继承复用和合成复用两种
继承复用虽然有简单和易实现的优点,但它也存在以下缺点。

  1. 继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用。
  2. 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。
  3. 它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。

采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它有以下优点。
1.它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用。
2.新旧类之间的耦合度低。这种复用所需的依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口。
3.复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。

合成复用原则的实现方法

合成复用原则是通过将已有的对象纳入新对象中,作为新对象的成员对象来实现的,新对象可以调用已有对象的功能,从而达到复用

//尽量使用组合或者聚合,而不是使用继承
class Cooker{
  cook(){

  }
}
class Person{
  private cooker:Cooker
  cook(){
    this.cooker.cook()
  }
}

【例1】汽车分类管理程序。

分析:汽车按“动力源”划分可分为汽油汽车、电动汽车等;按“颜色”划分可分为白色汽车、黑色汽车和红色汽车等。如果同时考虑这两种分类,其组合就很多。图 1 所示是用继淨:关系实现的汽车分类的类图


图1

从图 1 可以看出用继承关系实现会产生很多子类,而且增加新的“动力源”或者增加新的“颜色”都要修改源代码,这违背了开闭原则,显然不可取。但如果改用组合关系实现就能很好地解决以上问题,其类图如图 2 所示。


图2

8.总结

tips:如何写出好代码

  • 可维护性,bug是否好改
  • 可读性,是否容易看懂
  • 可扩展性 是否可以添加新功能
  • 灵活性 添加新功能是否容易,老方法和接口是否容易复用
  • 简洁性,代码是否简单清晰
  • 可复用性 相同的代码不要写两遍
  • 可测试性 是否方便写单元测试和集成测试

23种设计模式

一. 创建型
  • 工厂模式(工厂方法模式、抽象工厂模式、简单工厂模式)、建造者模式、单例模式
  • 原型模式
二. 结构型
  • 代理模式、桥接模式、装饰器模式、适配器模式
  • 外观模式、组合模式、享元模式
三.行为型

-观察者模式、模版方法模式、策略模式、职责链模式、迭代器模式、状态模式

  • 访问者模式、 备忘录模式、命令模式、解释器模式、中介者模式
设计模式之间的关系.jpg
上一篇下一篇

猜你喜欢

热点阅读