接口的本质是契约(contract),双方都可见。
求数组中的数字之和和平均数
不同类型的数组,不同方法
using System;
using System.Collections;
namespace March9
{
internal class Program
{
public 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(nums1));
}
static int Sum(int [] nums)
{
int sum = 0;
foreach (var n in nums)
{
sum += n;
}
return sum;
}
static double Avg(int[] nums)
{
int sum = 0;
double count = 0;
foreach (var n in nums)
{
sum += n;
count++;
}
return sum / count;
}
}
}
利用接口
using System;
using System.Collections;
namespace March9
{
internal class Program
{
public static void Main(string[] args)
{
int[] nums1 = new int[] {1, 2, 3, 4, 5};
ArrayList nums2 = new ArrayList {1, 2, 3, 4, 5,6};//ArrayList,array继承自IEnumerable接口
Console.WriteLine(Sum(nums2));
Console.WriteLine(Avg(nums2));
}
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;
}
}
}
接口是为松耦合而生的,方便功能的可替换性,
using System;
using System.Collections;
namespace March9
{
internal class Program
{
public static void Main(string[] args)
{
var user=new User(new VIVO());//实现用户使用不同的手机打电话,发信息
user.Userphone();
}
}
class User
{
private IPhone _phone;
public User(IPhone phone)//接收接口类型的变量
{
_phone = phone;
}
public void Userphone()
{
_phone.Dial();
_phone.Pickup();
_phone.Send();
_phone.Receive();
}
}
interface IPhone
{
void Dial();//拨号
void Pickup();//接听
void Send();//发送
void Receive();//接收
}
class NokiaPhone:IPhone
{
public void Dial()
{
Console.WriteLine("Nokia is calling");
}
public void Pickup()
{
Console.WriteLine("hello,tim");
}
public void Send()
{
Console.WriteLine("Nokia is ring");
}
public void Receive()
{
Console.WriteLine("hello,maray");
}
}
class VIVO:IPhone
{
public void Dial()
{
Console.WriteLine("VIVO is calling");
}
public void Pickup()
{
Console.WriteLine("hello");
}
public void Send()
{
Console.WriteLine("Vivo is send");
}
public void Receive()
{
Console.WriteLine("Vivo is receiving");
}
}
}
语言对面向对象设计的内建支持:依赖反转,接口隔离,开闭原则……
依赖反转
被依赖的再下面,上面是司机下面的是车。Driver里有一个car类型的字段,他们是紧耦合的。这种情况下每一个人只能开对应的车,不能开其他的车
Driver里的字段不再是car等车的类型,而是一个基接口,去调用各种车里的run方法,出现了依赖反转。
当多个服务的提供者和服务的使用者都遵循一个接口时,就可以进行多种配对。通过接口作为桥梁,实现调用不同实例的方法。
接口隔离
契约:甲方(派生类)不会多要,乙方(接口)不会少给。如果一直没有调用,就说明他是多余的,是个胖接口。
单一职责原则,一个类只做一件事。
例子:让一个人开多种交通工具
using System;
namespace March10
{
internal class Program
{
public static void Main(string[] args)
{
var a=new Driver(new Car());//此时car类和Truck都能开汽车,因为他继承自IVhicle接口,如果我想开坦克呢?那么就要改变Driver引用实例的变量的类型。
a.Drive();
}
}
class Driver
{
private IVehicle _vehicle;//此时,IVehicle类型的变量能引用继承自IVhicle接口实例的方法;ITank类型的变量能引用继承自ITank接口的实例的方法
public Driver(IVehicle vehicle)
{
_vehicle = vehicle;
}
public void Drive()
{
_vehicle.Run();
}
}
interface IVehicle
{
void Run();
}
class Car:IVehicle
{
public void Run()
{
Console.WriteLine("car is runnning!");
}
}
class Truck:IVehicle
{
public void Run()
{
Console.WriteLine("truck is running!");
}
}
interface ITank
{
void Fire();
void Run();
}
class LightTank:ITank
{
public void Fire()
{
Console.WriteLine("开炮");
}
public void Run()
{
Console.WriteLine("ka……ka……ka");
}
}
}
上面这种换来换取的方法很麻烦,问题就是传进去了胖接口,fire()方法用不到,因为功能不同,我只想开车,而不是开火做其他的事。
把fire()方法单独做一个接口,然后让ITank接口继承两个基接口。接口是可以多重继承的,而类不行。
using System;
namespace March10
{
internal class Program
{
public static void Main(string[] args)
{
var a=new Driver(new Car());
a.Drive();
}
}
class Driver
{
private IVehicle _vehicle;
public Driver(IVehicle vehicle)
{
_vehicle = vehicle;
}
public void Drive()
{
_vehicle.Run();
}
}
interface IVehicle
{
void Run();
}
interface Single
{
void Fire();
}
class Car:IVehicle
{
public void Run()
{
Console.WriteLine("car is runnning!");
}
}
class Truck:IVehicle
{
public void Run()
{
Console.WriteLine("truck is running!");
}
}
interface ITank:IVehicle,Single
{
}
class LightTank:ITank
{
public void Fire()
{
Console.WriteLine("开炮");
}
public void Run()
{
Console.WriteLine("ka……ka……ka");
}
}
}
此时,我们调用ITank接口的时候,只关注了里面的继承自IVhicle的run方法,Driver只调用run方法,服务的调用者不会多要。
问题又来了,如果我们把Driver类里的改为private ITank _vehicle;构造器的变量类型改变了,那么会发现,car和truck类的run方法无法调用,因为接口用的太大了,应该传小接口。
using System;
using System.Collections;
namespace March10
{
internal class Program
{
public static void Main(string[] args)
{
int[] nums1 = new int[] {1, 2, 3, 4, 5};
ArrayList nums2 = new ArrayList {1, 2, 3, 4,5,6};//ArrayList,array继承自IEnumerable接口
var nums3=new ReadOnlyCollection(nums1);
Console.WriteLine(Sum(nums1));
foreach (var n in nums3)
{
Console.WriteLine(n);
}
}
static int Sum(ICollection nums)
{
int sum = 0;
foreach (var n in nums)
{
sum +=(int) n;
}
return sum;
}
}
public class ReadOnlyCollection:IEnumerable
{
private int[] _array;
public ReadOnlyCollection(int[] array)
{
_array = array;//接受过来的数组只读
}
public IEnumerator GetEnumerator()
{
return new Enumerator(this);
}
//成员类
public class Enumerator:IEnumerator
{
private ReadOnlyCollection _collection;
private int _head;
public Enumerator(ReadOnlyCollection collection)
{
_collection = collection;
_head = -1;
}
public bool MoveNext()
{
if (++_head < _collection._array[_head])
{
return true;
}
else
{
return false;
}
}
public void Reset()
{
_head = -1;
}
public object Current
{
get
{
object o = _collection._array[_head];
return o;
}
} //只读属性
}
}
}
sum方法里需要循环然后再加,所以参数只需要迭代就可以了,但是他的参数类型设计成了IColllection,导致符合迭代要求的IEnumerable类型的参数无法传进来,就这是所谓的“使用方要求过多”。
改为static int Sum(IEnumerable nums) { }就可以了
接口的显示与隐式
using System;
namespace March11
{
internal class Program
{
public static void Main(string[] args)
{
var kill=new Kill1();
kill.love();//此时看不到kill方法
IKiller k = kill;
k.kill();//此时看不到love方法
}
}
interface IGentleman
{
void love();
}
interface IKiller
{
void kill();
}
class Kill1 : IGentleman,IKiller
{
public void love()
{
Console.WriteLine("I love you");
}
void IKiller.kill()
{
Console.WriteLine("I will kill you");
}
}
}