public interface IShoulders {
public void move();
}
public class Body {
public void createBody(){
RightHand rightHand = new RightHand();
rightHand.move();
IShoulders leftHand = new LeftHand();
leftHand.move();
}
}
public class RightHand implements IShoulders {
public void move(){
System.out.println("Move Right Hand");
}
}
public class LeftHand implements IShoulders {
public void move(){
System.out.println("Move Left Hand");
}
}
public class Main {
public static void main(String[] args){
Body myBody1 = new Body();
myBody1.createBody();
Body myBody2 = new Body();
myBody2.createBody();
}
}
请在课程body
中解释当使用IShoulder leftHand = new LeftHand();
与RightHand rightHand = new RightHand();
进行比较时,封装是如何不同的,他们都在做同样的事情,所以何时使用什么以及为什么重要的?谢谢
答案 0 :(得分:0)
归结为访问字段时,您可以在立即输入字段时访问该字段。在这种情况下,如果您将某个字段键入为接口:IShoulders
,则只能直接访问该接口定义的任何属性/方法。当您访问定义为实现相同接口的类的字段时,此新字段(定义和访问为RightHand
)不仅可以访问由IShoulders
定义的属性/方法,还可以访问扩展属性/类RightHand
定义的方法。
根据您的定义,除了move()
打印出来的值之外,这里没有真正的区别,但请进行以下更改:
public class RightHand implements IShoulders {
public void move(){
System.out.println("Move Right Hand");
}
public void Gesture()
{
System.out.println("Make a gesture with Right Hand");
}
}
新Gesture
方法只能由定义为RightHand
的字段访问,即使您使用IShoulders
定义字段,它也不会存在于new RightHand()
定义的字段上RightHand
,您需要将字段打包或转换为RightHand
以访问新方法。
鉴于上述IShoulders shoulder = new RightHand();
shoulder.move(); // this will work fine...
shoulder.Gesture(); // this will not be possible as "Gesture()" does not exist on IShoulders.
((RightHand)shoulder).Gesture(); // this will work after casting shoulder as RightHand
// since the underlying object is actually a RightHand.
类定义,请尝试以下方法:
RightHand rhand = new RightHand();
rhand.move(); // again, this works fine...
rhand.Gesture(); // this now works fine too..
然后尝试以下方法:
virtual
此外,该接口是您实现它的任何类的合同。它只是说如果一个类实现了一个接口,它必须实现该接口中的所有内容。然而,它没有说明如何实施它。因此,只要该类实现了接口的结构,这些实现的内容就可以完全自定义到每个单独的类。
实现接口的替代方法是使用子类创建派生类。这看起来类似于实现接口,但是基类已经具有属性和方法的实现。它们可以被覆盖,除非你不用final
装饰它们,或者在另一个继承级别覆盖并使用public string HyphenateTwoWords(string word1, string word2)
{
return string.Format("{0}-{1}", word1, word2);
}
装饰。
对不起,但是让我们说清楚,因为它仍然有点令人困惑。 当我写IShoulder rightHand或RightHand rightHand i; m define 创建之前对象的类型?我也一直想问 如果我们正在使用new关键字,我们需要定义一个类型 就像我们对集合之类的任何其他对象一样?列表<> mylist = 新列表<>以及如何口头解决这个问题?喜欢对象权利的手 class RightHand封装RightHand / IShoulder?如果我是正确的我 错了 - eersteam
当然,对于键入变量 - 这允许编译器(和程序员,就此而言)强烈定义可以使用的内容,以及如何在对象/值方面使用它。一个简单的例子是函数:
string
上面的函数接受两个string
参数并将它们连接在一起,并在它们之间用连字符连接。如果参数没有输入为string
,则可以传入任何内容,在某些情况下结果可能完全错误,或者在其他情况下抛出异常。由于它们被定义为string.Format()
,var
调用执行时没有任何问题。
现在,在C#和其他语言中,您实际上可以使用List<string> myStringList = new List<string>();
myStringList.Add("Test1");
myStringList.Add("Test2");
myStringList.Add("Test3");
foreach(var item in myStringList)
Console.WriteLine(item);
来定义字段/变量,例如:
item
这使编译器可以根据分配给它的内容推断var
的实际类型。但是,在大多数情况下,您真的不想使用AddLegs()
。如果没有其他原因,最好显式键入变量,可读性。可以更容易地看到变量的类型,特别是当您使用由多个开发人员维护的代码时。另外,显式键入变量可确保将按预期使用,否则会抛出可处理的异常,或者编译器在构建解决方案时会抛出错误或警告。
现在,就上述评论而言:
......我怎么知道它的左手还是右手? 在我的例子中,手不是肩膀,但手是假设的 附着在应该因为肩膀使它们能够移动。 那你将如何实现呢? - eersteam
让我们设计一些界面和类来说明它们的用途,我将对你已经完成的所有这些进行一些更改/添加应该是相当直接的 - 看一下Body
方法LeftLeg
课程,并查看public class BodyConstructor
{
public interface IBody
{
int Age { get; set; }
string Species { get; set; }
BodyPartGroup Head { get; set; }
BodyPartGroup Thorax { get; set; }
BodyPartGroup Abdomen { get; set; }
BodyPartGroup Pelvis { get; set; }
}
public interface IBodyPartGroup
{
BodyPartGroupType Type { get; set; }
List<IBodyPart> BodyParts { get; set; }
}
public interface IBodyPart
{
LateralDirection LateralOrientation { get; set; }
string Name { get; set; }
List<IBodyPart> SubParts { get; set; }
void Move();
}
public enum BodyPartGroupType
{
Head = 1,
Thorax = 2,
Abdomen = 3,
Pelvis = 4,
}
public enum LateralDirection
{
Central = 0,
Right = 1,
Left = 2,
}
public class Body : IBody
{
public Body()
{
Head = new BodyPartGroup(BodyPartGroupType.Head);
Thorax = new BodyPartGroup(BodyPartGroupType.Thorax);
Abdomen = new BodyPartGroup(BodyPartGroupType.Abdomen);
Pelvis = new BodyPartGroup(BodyPartGroupType.Pelvis);
}
public Body(int age, string species)
: this()
{
Age = age;
Species = species;
}
public int Age { get; set; }
public string Species { get; set; }
public BodyPartGroup Head { get; set; }
public BodyPartGroup Thorax { get; set; }
public BodyPartGroup Abdomen { get; set; }
public BodyPartGroup Pelvis { get; set; }
public void AddLimbs()
{
AddArms();
AddLegs();
}
public void AddArms()
{
// Individual segments for the left arm
BodyPart leftShoulder = new BodyPart("Left Shoulder", LateralDirection.Left);
BodyPart leftArm = new BodyPart("Left Arm", LateralDirection.Left);
BodyPart leftHand = new BodyPart("Left Hand", LateralDirection.Left);
leftArm.SubParts.Add(leftHand);
leftShoulder.SubParts.Add(leftArm);
// Individual segments for the right arm
BodyPart rightShoulder = new BodyPart("Right Shoulder", LateralDirection.Right);
BodyPart rightArm = new BodyPart("Right Arm", LateralDirection.Right);
BodyPart rightHand = new BodyPart("Right Hand", LateralDirection.Right);
rightArm.SubParts.Add(rightHand);
rightShoulder.SubParts.Add(rightArm);
// Add arms to thorax
Thorax.BodyParts.Add(rightShoulder);
Thorax.BodyParts.Add(leftShoulder);
}
public void AddLegs()
{
// Individual segments for the left leg
BodyPart leftHip = new BodyPart("Left Hip", LateralDirection.Left);
LeftLeg leftLeg = new LeftLeg(); // Here we use the LeftLeg class instead, which inherits BodyPart
BodyPart leftFoot = new BodyPart("Left Foot", LateralDirection.Left);
leftLeg.SubParts.Add(leftFoot);
leftHip.SubParts.Add(leftLeg);
//Individual segments for the right leg
BodyPart rightHip = new BodyPart("Right Hip", LateralDirection.Right);
BodyPart rightLeg = new BodyPart("Right Leg", LateralDirection.Right);
BodyPart rightFoot = new BodyPart("Right Foot", LateralDirection.Right);
rightLeg.SubParts.Add(rightFoot);
rightHip.SubParts.Add(rightLeg);
// Add legs to pelvis
Pelvis.BodyParts.Add(leftHip);
Pelvis.BodyParts.Add(rightHip);
}
}
public class BodyPartGroup : IBodyPartGroup
{
public BodyPartGroup()
{
BodyParts = new List<IBodyPart>();
}
public BodyPartGroup(BodyPartGroupType type)
: this()
{
this.Type = type;
}
public BodyPartGroupType Type { get; set; }
public List<IBodyPart> BodyParts { get; set; }
}
public class BodyPart : IBodyPart
{
public BodyPart()
{
SubParts = new List<IBodyPart>();
}
public BodyPart(string name, LateralDirection orientation)
: this()
{
Name = name;
LateralOrientation = orientation;
}
// Location of body part: Left, Central, Right
public LateralDirection LateralOrientation { get; set; }
public string Name { get; set; }
public List<IBodyPart> SubParts { get; set; }
public virtual void Move()
{
// Common body part movement code.
}
}
public class LeftLeg : BodyPart
{
public LeftLeg()
{
Name = "Left Leg";
LateralOrientation = LateralDirection.Left;
}
public override void Move()
{
// Custom left leg move code
base.Move(); // Call base BodyPart.Move();
}
}
}
课程。
{{1}}
答案 1 :(得分:0)
在您的示例中,leftHand是LeftHand,rightHand是RightHand。您创建的两个对象都是实现IShoulder的类的实例。这两个都是ISHI背景,因此可以'#34;移动&#34;。 leftHand和rightHand 变量之间的区别是你可以将leftHand设置为RightHand类的实例,因为leftHand被声明为IShoulder,它可以是。如果你这样做,然后调用leftHand.move(),它会移动右手。得到它?
界面带给你的是能够使用任何一只手而不需要知道你实际使用的是哪只手。在您的示例中,如果您将IShoulder接口命名为IHand然后命名为IHand变量hand而不是leftHand,那么它将更有意义。你的例子暗示双手是肩膀。
但那不是封装的内容。封装只是简单地将相关数据和作用于该数据的方法放在一起,并限制外部访问仅有助于其基本特征的组件(即隐藏细节)。因此Body类可以封装头部,手臂,腿部,躯干等。并且手臂可以封装肩部,上臂,肘部,下臂和手。从外面看到的所有东西都是一个带有部件的手臂,可以做你期望手臂做的事情。您不希望在不了解或关心其工作方式的情况下使用Arm类。这是一个例子:
// the arm class encapsulates the parts of the arm (the data), and the behavior of the arm (the methods).
class Arm
{
Shoulder shoulder = new Shoulder();
Hand hand = new Hand();
UpperArm upperArm = new UpperArm();
Elbow elbow = new Elbow();
LowerArm lowerArm = new LowerArm();
void poke()
{
flinch();
}
void flinch()
{
// write some code that flinches the arm
}
void move()
{
// move the arm all over the place
shoulder.move();
hand.wave();
doHighFive();
//...
}
void doHighFive()
{
// move all the arm parts in a high-five motion
}
}
// the Body class encapsulates the parts of the body (the data), and the behavior of the body (the methods).
class Body
{
Head head = new Head();
Torso torso = new Torso();
Arm leftArm = new Arm();
Arm rightArm = new Arm();
Leg leftLeg = new Leg();
Leg rightLeg = new Leg();
void poke()
{
flinch();
dance();
System.out.println("I didn't flinch, I was just dancing! Now I'm tired, though.");
goToBed();
}
void flinch()
{
// write some code that makes the body flinch
}
void dance()
{
// write code to make the body dance
}
void goToBed()
{
// write code to make the body go to bed
}
}
但是,您的问题似乎更多地是关于何时使用接口而不是实现该接口的类之一。这更像是关于抽象数据类型的问题,而不是封装。抽象数据类型仅由其行为定义(无代码或数据)。接口是一种定义抽象数据类型的方法。它只是一个方法签名列表;把它想象成一个实施者必须遵守的合同。
那你什么时候使用界面?作为一个例子,如果我的工作是迭代一组身体部位和&#34; poke&#34;他们,我真的不在乎你给我什么特定类型的身体部位,我只需要戳他们。这是一个例子:
// IBodyPart defines an abstract data type that can be "poked", among other things.
interface IBodyPart
{
void poke();
//...
}
// maybe hands handle being poked by slapping
class Hand implements IBodyPart
{
void poke()
{
slap();
}
void slap()
{
// write some code that slaps
}
}
// maybe shoulders just flinch
class Shoulder implements IBodyPart
{
void poke()
{
flinch();
}
void flinch()
{
// write some code to flinch the shoulder
}
}
// etc. Each IBodyPart class has to implement the poke method.
// I don't know nor do I care what I'm poking. I can do my job easily thanks to abstract data types!
class Me
{
void PokeEmAll(List<IBodyPart> bodyPartList)
{
for (IBodyPart bodyPart : bodyPartList)
{
bodyPart.poke();
}
}
}
class Main
{
// create a list of body parts so I can poke 'em all
static void main()
{
List<IBodyPart> bodyPartList = new List<IBodyPart>();
Hand leftHand = new Hand();
bodyPartList.add(leftHand);
Hand rightHand = new Hand();
bodyPartList.add(rightHand);
Shoulder leftShoulder = new Shoulder();
bodyPartList.add(leftShoulder);
Shoulder rightShoulder = new Shoulder();
bodyPartList.add(rightShoulder);
Me me = new Me();
me.PokeEmAll(bodyPartList);
}
}