假设我有Namespace
类如下:
abstract class Namespace {
protected prefix;
protected Map<String, String> tags;
public setTag(String tagKey, String tagValue) {
tags.put(tagKey, tagValue);
}
}
我被告知,一个好的设计是每个命名空间都有单独的实现,其中getter定义了从tags
映射中检索的信息。即
class FooNamespace extends Namespace {
public String getNameTag() {
return tags.get("name");
}
}
我们可以单独实现像BarNamespace
这样的命名空间,它会存储不同的标记(BarNamespace
没有ex的名称标记,而是有一个年龄标记)。为什么上述设计比简单地让简单Namespace
类的使用者请求他们想要的标记更有意义。即:
class Namespace {
private prefix;
private Map<String, String> tags;
public String getTag(String key) {
return tags.get(key);
}
}
以上用作
Namespace.getTag("name");
答案 0 :(得分:0)
答案很大程度上取决于你希望实现的目标 - 但如果我必须输入getTag("name")
几百次,我一定会犯错误。
getNameTag
取消了一些猜测工作,降低了我在没有注意到的情况下输入"name"
错误的可能性。它还减少了我需要的关于API的知识量 - 我知道我可以获得name
标签的价值,但我不需要知道它是如何实际实现的 - 它可能会实施之间的变化。
它是一个好的设计&#34;是一个意见问题,取决于如何使用目标类。是&#34;名称&#34;在应用程序中常见的有用吗?怎么样&#34; date&#34;或&#34;数字&#34;值 - 一些辅助方法可能很好;)
有一点让我感到困惑的是,虽然Bar没有&#34; name&#34;的访问器,但是它由一个包含原始Map的抽象Namespace类支持,因此任何人都可以推入&# 34;名称&#34;通过执行Bar.putTag标记(&#34;名称&#34;,&#34; nameValue&#34;);关于如何实施&#34; setter&#34;?
的任何想法
这也是我对集合API的一般错误。
您可以创建一个不可变且可变的概念......
public interface Namespace {
public String getTag(String key);
}
public interface MutableNamespace extends Namespace {
public void setTag(String key, String value);
}
然后你可以开始抽象那些......
public abstract class AbstractNamespace implements MutableNamespace {
private prefix;
private Map<String, String> tags;
public setTag(String tagKey, String tagValue) {
tags.put(tagKey, tagValue);
}
public String getTag(String key) {
return tags.get(key);
}
}
最后为可能在......中使用的上下文提供有用的实现。
public interface MySuperHelpfulNamespace extends Namespace {
public String getNameTag();
}
public class DefaultMySuperHelpfulNamespace extends AbstractNamespace implements MySuperHelpfulNamespace {
public String getNameTag() {
return tags.get("name");
}
}
然后写你的app来支持他们......
public void someMethodWhichDoesNotNeedToChangeTheValues(MySuperHelpfulNamespace namespace) {
//...
}
public void someMethodWhichDoesNeedToChangeTheValues(MutableNamespace namespace) {
//...
}
这实际上是&#34;编码到接口(不是实现)
的一个例子答案 1 :(得分:0)
您尝试通过继承重用Namespace
的财产:Map
。
告诉对象执行行为。
隐藏内部状态并要求通过对象的方法执行所有交互被称为数据封装 - 面向对象编程的基本原则。
当ClassA
扩展ClassB
时,子类型ClassA
应该继承它的超类型{{1}的行为或公共接口}}。 您的ClassB
类型似乎无法定义任何行为。
Namespace
或setter
视为行为,但不要被愚弄。目前没有理由让开发人员更喜欢使用getter
类型,而不仅仅使用Namespace
,而不是将他们使用的界面从Map
更改为get
。
getTag
&amp; getter
方法违反了OOP。随意read up on the discussion。
应该如何setter
实际使用?它有什么要求?应Namespace
定义哪些行为?
您可以通过分析Namespace
的使用方式来回答这个问题。举个例子,也许您打算将它附加到getter
以形成一些XML。
想象一下你的要求是......
将来自各种XML标记集的单个标记附加到将用于呈现的StringBuilder。
而不是......
StringBuilder
StringBuilder builder = ...;
Namespace namespace = ...;
builder.append(namespace.getTag("name"));
可以负责附加 标记(Namespace
是Namespace
存储的所有者标签)对任何请求它。
Map
请注意public final class Namespace {
private final Map<String, String> tags;
public Namespace(Map<String, String> tags) {
this.tags = tags;
}
// if you REALLY need to add tags after instantiation
public Namespace addTag(String key, String value) {
Map<String, String> tags = new HashMap<>(this.tags);
tags.put(key, value);
return new Namespace(tags);
}
// the behavior that fufills the requirement
public void appendTo(StringBuilder builder, String key) {
builder.append(tags.get(key));
}
}
行为(附加到Namespace
),而不是StringBuilder
的代理。
Map
我没有找到创建子类型的原因(不强迫它)。只有在必须扩展超类型的行为时,才应创建子类型。如果StringBuilder builder = ...;
Namespace namespace = ...;
namespace.appendTo(builder, "name");
没有向BarNamespace
添加任何功能,则不需要它。
创建Namespace
以涵盖帖子公开的功能后,我不需要子类型。似乎所有内容都由Namespace
优雅地处理。
您没有指定您的要求,但希望此答案将指导您确定它们(基于您如何使用Namespace
)并以面向对象的方式实施它们。 / p>
正如@MadProgrammer所指出的那样:
如果我必须输入几百次getTag(&#34; name&#34;),我一定会犯错误。
如果您发现自己需要经常输入getter
,那么您可能希望包含一种类型安全的方式来执行该行为。
在这种情况下,您可能更喜欢composition over inheritance。您可以创建引用"name"
的类型来执行您想要执行的操作,而不是使子类型扩展Namespace
。
Namespace
你可以说&#34; 等等!我想把public final class Bar {
private final Namespace namespace;
public Bar(Namespace namespace) {
this.namespace = namespace;
}
public void appendNameTo(StringBuilder builder) {
namespace.appendTo(builder, "name");
}
}
传递给预期Bar
的地方!&#34;
这没有意义。如果代码依赖于Namespace
,那么即使在您的代码中也不会出现类型安全性。例如:
Namespace
除非您向void doSomething(Namespace namespace) {
}
投放或声明appendNameTo
,否则您无法访问Namespace
中定义的任何方法。你说子类型可能有不同的标签。这意味着如果您要进行类型安全,您的子类型将具有不同的公共接口,因此我没有扩展Bar
。