網易元宇宙要來了 2022年1月1日揭曉 和大話西遊有關
抽象類
類和方法前加上abstract就變成抽象的
抽象方法不包容任何實現程式碼,不能是private
無法使用new關鍵字直接建立抽象類的物件。只能例項化子類物件
包含抽象方法的類一定是抽象類,但抽象類中的方法不一定是抽象方法,抽象類中可以包容“普通的”方法。
抽象類專為做基類而生的
開閉原則(Open Close Principle)
開閉原則的意思是:對擴充套件開放,對修改關閉。在程式需要進行拓展的時候,不能去修改原有的程式碼,實現一個熱插拔的效果。簡言之,是為了使程式的擴充套件性好,易於維護和升級。想要達到這樣的效果,我們需要使用介面和抽象類。
using System; namespace AbstractStudy { //為做基類而生的“抽象類”與“開放/關閉原則” class Program { static void Main(string[] args) { Vehicle v = new Truck(); v.Run(); } } abstract class Vehicle { public void Stop() { Console.WriteLine("Stopped!"); } //抽象方法,不需要宣告方法體,事實上就算不是抽象方法,是虛方法的話,方法體也用不上,所以建議直接使用抽象方法 public abstract void Run(); } class Car :Vehicle { public override void Run() { Console.WriteLine("Car is running..."); } } class Truck :Vehicle { public override void Run() { Console.WriteLine("Truck is running..."); } } }
此例中展示了抽象類的好處,滿足了開閉原則,當程式需要擴充套件時,只需要新增子類或者重寫抽象方法即可,不需要更改抽象類
介面:更抽象的抽象類
使用interface關鍵字定義介面,介面定義了所有類繼承介面時應遵循的語法合同。介面定義了語法合同 "是什麼" 部分,派生類定義了語法合同 "怎麼做" 部分。
介面定義了屬性、方法和事件,這些都是介面的成員。介面只包含了成員的宣告。成員的定義是派生類的責任。介面提供了派生類應遵循的標準結構。
介面命名一般以I開頭,成員全部public
介面就等於純虛的抽象類(抽象類中的方法全是抽象的)
介面的好處1
using System; using System.Collections; namespace InterfaceStudy { class Program { static void Main(string[] args) { int[] nums1 = new int[] { 1, 2, 3, 4, 5 }; ArrayList nums2 = new ArrayList { 1, 2, 3, 4, 5 }; Console.WriteLine(Sum(nums1)); Console.WriteLine(Avg(nums2)); } //如果不使用介面,那麼求和和求平均值要寫四個方法,因為int和arraylist是強型別,不能通用 //需求方的foreach要求資料能夠被迭代 //arraylist和int都遵守IEnumerable介面,可以被迭代 static int Sum(IEnumerable nums) { int sum = 0; foreach (var n in nums) sum += (int)n; return sum; } static double Avg(IEnumerable nums) { int sum = 0; double count=0; foreach (var n in nums) { sum +=(int)n; count++; } return sum/count; } } }
介面的好處2:
緊耦合的例子:
using System; using System.Collections; namespace InterfaceStudy { class Program { static void Main(string[] args) { var engine = new Engine(); var car = new Car(engine); car.Run(3); Console.WriteLine(car.Speed); } } //car和engine耦合,Car非常依賴於Engine,弊端很大 class Engine { public int RPM { get; private set; } public void Work(int gas) { this.RPM = 1000 * gas; } } class Car { private Engine _engine; public Car(Engine engine) { _engine = engine; } public int Speed { get; private set; } public void Run(int gas) { _engine.Work(gas); this.Speed = _engine.RPM / 100; } } }
日常開發中要避免緊耦合
解決耦合的方法可以使用介面!
using System;
using System.Collections;
namespace InterfaceStudy
{
class Program
{
static void Main(string[] args)
{
//諾基亞和愛立信實現瞭解耦合,只需改變呼叫手機的名字,而介面和類的程式碼不需要更改
var user = new PhoneUser(new EricsssonPhone());
var user = new PhoneUser(new NokiaPhone());
user.UsePhone();
}
}
//介面
interface IPhone
{
void Dail();
void PickUp();
void Send();
void Receive();
}
class PhoneUser
{
private IPhone _phone;
public PhoneUser(IPhone phone)
{
_phone = phone;
}
public void UsePhone()
{
_phone.Dail();
_phone.PickUp();
_phone.Receive();
_phone.Send();
}
}
//諾基亞
class NokiaPhone : IPhone
{
public void Dail()
{
Console.WriteLine("Nokia calling......");
}
public void PickUp()
{
Console.WriteLine("Hello!This is Tim!");
}
public void Receive()
{
Console.WriteLine("Nokia message ring......");
}
public void Send()
{
Console.WriteLine("Hello!");
}
}
//愛立信手機
class EricsssonPhone : IPhone
{
public void Dail()
{
Console.WriteLine("Ericssson calling......");
}
public void PickUp()
{
Console.WriteLine("Hi!This is Tim!");
}
public void Receive()
{
Console.WriteLine("Ericssson ring......");
}
public void Send()
{
Console.WriteLine("Good evening!");
}
}
}
介面,解耦在單元測試中的應用
先寫出緊耦合的程式碼
using System;
using System.Collections;
namespace InterfaceStudy
{
class Program
{
static void Main(string[] args)
{
var fan = new DeskFan(new PowerSupply());
Console.WriteLine(fan.Work());
}
}
//電源
class PowerSupply
{
public int GetPower()
{ return 100; }
}
//電扇
class DeskFan
{
private PowerSupply _powerSupply;
public DeskFan(PowerSupply powerSupply)
{
_powerSupply = powerSupply;
}
public string Work()
{
int power = _powerSupply.GetPower();
if (power < 0)
{
return "Won't work";
}
else if (power < 100)
{ return "Slow"; }
else if (power < 200)
{ return "Work fine"; }
else
{ return "Warning!"; }
}
}
}
//此程式碼,如果需要測試不同電源電壓時程式碼輸出是否正確,那麼就需要不斷的更改PowerSupply類
//這樣是不對的,因為類設計出來就不應該再被更改,所以需要介面來解耦合。
//更改成下面這樣的程式碼
interface IpowerSupply
{
int GetPower();
}
//電源
class PowerSupply:IpowerSupply
{
public int GetPower()
{ return 100; }
}
//電扇
class DeskFan
{
private IpowerSupply _powerSupply;
public DeskFan(IpowerSupply powerSupply)
{
_powerSupply = powerSupply;
}
//再寫單元測試來測試程式碼是否正確
介面隔離原則
介面隔離原則(Interface Segregation Principle,ISP)要求程式設計師儘量將臃腫龐大的介面拆分成更小的和更具體的介面,讓介面中只包含客戶感興趣的方法。
要為各個類建立它們需要的專用介面,而不要試圖去建立一個很龐大的介面供所有依賴它的類去呼叫。
介面隔離原則和單一職責都是為了提高類的內聚性、降低它們之間的耦合性,體現了封裝的思想,但兩者是不同的:
- 單一職責原則注重的是職責,而介面隔離原則注重的是對介面依賴的隔離。
- 單一職責原則主要是約束類,它針對的是程式中的實現和細節;介面隔離原則主要約束介面,主要針對抽象和程式整體框架的構建。
單一職責原則
單一職責原則的定義
單一職責原則(Single Responsibility Principle,SRP)
又稱單一功能原則,由羅伯特·C.馬丁(Robert C. Martin)於《敏捷軟體開發:原則、模式和實踐》一書中提出的。這裡的職責是指類變化的原因,單一職責原則規定一個類應該有且僅有一個引起它變化的原因,否則類應該被拆分(There should never be more than one reason for a class to change)。
該原則提出物件不應該承擔太多職責,如果一個物件承擔了太多的職責,至少存在以下兩個缺點:
- 一個職責的變化可能會削弱或者抑制這個類實現其他職責的能力;
- 當客戶端需要該物件的某一個職責時,不得不將其他不需要的職責全都包含進來,從而造成冗餘程式碼或程式碼的浪費。
單一職責原則的優點
單一職責原則的核心就是控制類的粒度大小、將物件解耦、提高其內聚性。如果遵循單一職責原則將有以下優點。
- 降低類的複雜度。一個類只負責一項職責,其邏輯肯定要比負責多項職責簡單得多。
- 提高類的可讀性。複雜性降低,自然其可讀性會提高。
- 提高系統的可維護性。可讀性提高,那自然更容易維護了。
- 變更引起的風險降低。變更是必然的,如果單一職責原則遵守得好,當修改一個功能時,可以顯著降低對其他功能的影響。