Ion*_*onR 17 c# oop composition generalization
这可能是一个简单/基本的OOP问题,但我仍然无法弄清楚如何解决它.我在访谈中遇到了以下问题:制作一个UML类图并编写一个"智能"手机的基本代码,其中包含电话和MP3播放器的功能.我们有以下(接受)解决方案:
class Telephone
{
public string name { get; set; }
public Telephone()
{
name = "name telephone";
}
}
class MP3
{
public string name { get; set; }
public MP3()
{
name = "name mp3";
}
}
Run Code Online (Sandbox Code Playgroud)
而"智能"手机类:
class TelephoneMP3
{
public Telephone tel;
public MP3 mp3;
public TelephoneMP3()
{
tel = new Telephone();
mp3 = new MP3();
}
}
Run Code Online (Sandbox Code Playgroud)
如您所见,我们在TelephoneMP3和Telephone/MP3类之间有一个组合关系.
但是,使用此代码,TelephoneMP3不是电话,而且TelephoneMP3也不是MP3,这是不合逻辑的.那么,为了使这个有效,我应该做些什么改变?例如,这种测试:
if (telMp3 is Telephone)
{
Console.WriteLine("TelephoneMP3 is telephone");
}
if (telMp3 is MP3)
{
Console.WriteLine("TelephoneMP3 is mp3");
}
Run Code Online (Sandbox Code Playgroud)
可以使用以下备注进行修改:
先感谢您
Bar*_*zek 35
由于C#不支持多重继承,因此请考虑使用接口:
public interface Phone{ ... }
public interface Mp3{ ... }
public class Telephone : Phone{ ... }
public class Mp3Player : Mp3{ ... }
public class Smartphone : Phone, Mp3{ ... }
Run Code Online (Sandbox Code Playgroud)
这种方式Smartphone是Phone和Mp3.如果您需要编写一个对a进行操作的方法Telephone,请改用该Phone接口.这样你就可以传递任何一个Telephone或Smartphone作为一个参数.
Eri*_*ert 18
这里有一些很好的答案.说使用界面的答案是好的,这就是面试官可能正在寻找的.但是,我会考虑简单地否定这样一个前提,即"是一种"关系得到满足是一个好主意.相反,我会考虑使用服务提供商组织:
public interface ITelephone { ... }
internal class MyTelephone : ITelephone { ... }
public interface IMusicPlayer { ... }
internal class MyPlayer : IMusicPlayer { ... }
public interface IServiceProvider
{
T QueryService<T>() where T : class;
}
internal class MyDevice : IServiceProvider
{
MyTelephone phone = new MyTelephone();
MyPlayer player = new MyPlayer();
public T QueryService<T>() where T : class
{
if (typeof(T) == typeof(ITelephone)) return (T)(object)phone;
if (typeof(T) == typeof(IPlayer)) return (T)(object)player;
return null;
}
}
Run Code Online (Sandbox Code Playgroud)
现在,呼叫者可以MyDevice通过其IServiceProvider界面进行操作.你问它
ITelephone phone = myDevice.QueryService<ITelephone>();
Run Code Online (Sandbox Code Playgroud)
如果phone是非null,则设备可以像手机一样工作.但
myDevice is ITelephone
Run Code Online (Sandbox Code Playgroud)
是假的.该设备不是手机,它知道如何找到像手机一样的东西.
有关此内容的更多信息,请研究MAF等插件架构.
G.Y*_*G.Y 17
它几乎与其他答案类似,但是......
我认为它在继承层次结构方面具有最佳准确性.
internal class Program
{
private static void Main(string[] args)
{
var telephone = new Telephone();
Console.WriteLine(telephone.Name);
telephone.OutboundCall("+1 234 567");
Console.WriteLine("Am I a Telephone? {0}", telephone is Telephone);
Console.WriteLine("Am I a MP3? {0}", telephone is MediaPlayer3);
Console.WriteLine("Am I a Smartphone? {0}", telephone is Smartphone);
Console.WriteLine("Do I Have Telephone Capabilities? {0}", telephone is ITelephone);
Console.WriteLine("Do I Have MP3 Capabilities? {0}", telephone is IMediaPlayer3);
Console.WriteLine();
var mp3 = new MediaPlayer3();
Console.WriteLine(mp3.Name);
mp3.PlaySong("Lalala");
Console.WriteLine("Am I a Telephone? {0}", mp3 is Telephone);
Console.WriteLine("Am I a MP3? {0}", mp3 is MediaPlayer3);
Console.WriteLine("Am I a Smartphone? {0}", mp3 is Smartphone);
Console.WriteLine("Do I Have Telephone Capabilities? {0}", mp3 is ITelephone);
Console.WriteLine("Do I Have MP3 Capabilities? {0}", mp3 is IMediaPlayer3);
Console.WriteLine();
var smartphone = new Smartphone();
Console.WriteLine(smartphone.Name);
smartphone.OutboundCall("+1 234 567");
smartphone.PlaySong("Lalala");
Console.WriteLine("Am I a Telephone? {0}", smartphone is Telephone);
Console.WriteLine("Am I a MP3? {0}", smartphone is MediaPlayer3);
Console.WriteLine("Am I a Smartphone? {0}", smartphone is Smartphone);
Console.WriteLine("Do I Have Telephone Capabilities? {0}", smartphone is ITelephone);
Console.WriteLine("Do I Have MP3 Capabilities? {0}", smartphone is IMediaPlayer3);
Console.ReadKey();
}
public interface IDevice
{
string Name { get; }
}
public interface ITelephone : IDevice
{
void OutboundCall(string number);
}
public interface IMediaPlayer3 : IDevice
{
void PlaySong(string filename);
}
public class Telephone : ITelephone
{
public string Name { get { return "Telephone"; } }
public void OutboundCall(string number)
{
Console.WriteLine("Calling {0}", number);
}
}
public class MediaPlayer3 : IMediaPlayer3
{
public string Name { get { return "MP3"; } }
public void PlaySong(string filename)
{
Console.WriteLine("Playing Song {0}", filename);
}
}
public class Smartphone : ITelephone, IMediaPlayer3
{
private readonly Telephone telephone;
private readonly MediaPlayer3 mp3;
public Smartphone()
{
telephone = new Telephone();
mp3 = new MediaPlayer3();
}
public string Name { get { return "Smartphone"; } }
public void OutboundCall(string number)
{
telephone.OutboundCall(number);
}
public void PlaySong(string filename)
{
mp3.PlaySong(filename);
}
}
}
Run Code Online (Sandbox Code Playgroud)
节目输出:
Telephone Calling +1 234 567 Am I a Telephone? True Am I a MP3? False AM I a Smartphone? False Do I Have Telephone Capabilities? True Do I Have MP3 Capabilities? False MP3 Playing Song Lalala Am I a Telephone? False Am I a MP3? True AM I a Smartphone? False Do I Have Telephone Capabilities? False Do I Have MP3 Capabilities? True Smartphone Calling +1 234 567 Playing Song Lalala Am I a Telephone? False Am I a MP3? False AM I a Smartphone? True Do I Have Telephone Capabilities? True Do I Have MP3 Capabilities? True
我认为这个面试问题不是(应该是所有面试问题)关于挑战本身.通过作文合并两个班级的编码练习可以用教科书来回答.这个挑战是一个微妙的技巧问题,我建议重点是让你讨论原因.至少这是我想从受访者那里得到的.
这个测试:
if(telMp3 is Telephone && telMp3 is MP3) {
Run Code Online (Sandbox Code Playgroud)
......是真正的问题.你为什么必须符合这个标准?该测试完全阻止了从构图中构建对象的目的.它要求以特定方式实现对象.它表明现有的类实现已经与代码库紧密耦合(如果它们无法完成).这些要求意味着没有遵循SOLID原则,因为您不能只实现基类型的方法,您必须实际上是基类型.那不好.
正如其他答案所说,解决方案是使用接口.然后,您可以将对象传递给任何需要该接口的方法.这种用法需要像这样的测试:
if (telMp3 is IPhone && telMp3 is IMp3) {
Run Code Online (Sandbox Code Playgroud)
......但由于挑战的局限性,你无法做到这一点.这意味着在其余的代码中,人们一直在编写明确依赖于特定类型Telephone和方法的方法MP3.这是真正的问题.
在我看来,这个挑战的正确答案是说代码库未通过测试.挑战中的具体影响是不可避免的; 在正确解决之前,您需要更改挑战的要求.一个认识到这个事实的受访者会通过测试,并且有很多颜色.
您也可以使用显式接口实现来限制共享变量的使用Name.这样你就必须转向界面才能访问它.您仍然可以从界面获得公共属性/方法.
仍然使用组合,但是SmartPhone可以控制其属性/方法的实现.
对我来说,这将是一起工作的最简单的实现,因为我很少想使用这两种从MP3播放器及手机执行,但他们中的相当一个.此外,我仍然可以完全控制在调用接口方法时发生的情况SmartPhone.
class User
{
void UseSmartPhone(SmartPhone smartPhone)
{
// Cannot access private property 'Name' here
Console.WriteLine(smartPhone.Name);
// Cannot access explicit implementation of 'IMp3Player.Play'
smartPhone.Play();
// You can send the phone to the method that accepts an IMp3Player though
PlaySong(smartPhone);
// This works fine. You are sure to get the Phone name here.
Console.WriteLine(((IPhone)smartPhone).Name);
// This works fine, since the Call is public in SmartPhone.
smartPhone.Call();
}
void CallSomeone(IPhone phone)
{
phone.Call();
}
void PlaySong(IMp3Player player)
{
player.Play();
}
}
class SmartPhone : IPhone, IMp3Player
{
private Phone mPhone;
private Mp3Player mMp3Player;
public SmartPhone()
{
mPhone = new Phone();
mMp3Player = new Mp3Player();
}
public void Call()
{
mPhone.Call();
}
string IPhone.Name
{
get { return mPhone.Name; }
}
string IMp3Player.Name
{
get { return mMp3Player.Name; }
}
void IMp3Player.Play()
{
mMp3Player.Play();
}
}
class Mp3Player
{
public string Name { get; set; }
public void Play()
{
}
}
class Phone
{
public string Name { get; set; }
public void Call()
{
}
}
interface IPhone
{
string Name { get; }
void Call();
}
interface IMp3Player
{
string Name { get; }
void Play();
}
Run Code Online (Sandbox Code Playgroud)
Sha*_*ler -5
C#不支持多重继承,需要使用接口和抽象类来实现共同的实现,可以这样做:
编辑:我已经在我的答案中添加了更多详细信息
abstract class BaseDevice
{
public string name { get; set; }
public void Print()
{
Console.WriteLine("{0}", name );
}
}
public interface IPhone
{
void DoPhone();
}
public interface IMP3
{
void DoMP3();
}
class Telephone :BaseDevice , IPhone
{
public Telephone()
{
name = "name telephone";
}
}
class MP3 : BaseDevice , IMP3
{
public MP3()
{
name = "name mp3";
}
}
class telMp3 : BaseDevice , IMP3, IPhone
{
private Telephone _tel;
private MP3 _mp3;
public telMp3()
{
name = "name telMp3";
}
public void DoPhone()
{
_tel.DoPhone();
}
public void DoMP3()
{
_mp3.DoMP3();
}
}
Run Code Online (Sandbox Code Playgroud)