官术网_书友最值得收藏!

Mapping our markdown tag types to HTML tag types

In our requirements, we set out a master list of tags that our parser is going to handle. In order to identify these tags, we are going to add an enumeration consisting of the tags we are making available to our users:

enum TagType {
Paragraph,
Header1,
Header2,
Header3,
HorizontalRule
}

From our requirements, we also know that we need to translate between these tags and their equivalent opening and closing HTML tags. The way that we are going to do this is to map tagType to an equivalent HTML tag. To do this, we are going to create a class that has the sole responsibility of handling this mapping for us. The following code shows this:

class TagTypeToHtml {
private readonly tagType : Map<TagType, string> = new Map<TagType, string>();
constructor() {
this.tagType.set(TagType.Header1, "h1");
this.tagType.set(TagType.Header2, "h2");
this.tagType.set(TagType.Header3, "h3");
this.tagType.set(TagType.Paragraph, "p");
this.tagType.set(TagType.HorizontalRule, "hr")
}
}
At first, the use of readonly on a type can appear confusing. What this keyword means is that, after the class has been instantiated, tagType cannot be recreated elsewhere in the class. This means that we can set up our mappings in the constructor safe, knowing that we are not going to call this.tagType = new Map<TagType, string>(); later on.

We also need a way to retrieve opening and closing tags from this class. We're going to start by creating a method to get the opening tag from tagType, as follows:

public OpeningTag(tagType : TagType) : string {
let tag = this.tagType.get(tagType);
if (tag !== null) {
return `<${tag}>`;
}
return `<p>`;
}

This method is pretty straightforward. It starts by trying to get tagType from the map. With the code we currently have, we will always have an entry in the map, but we could extend the enumeration in the future and forget to add the tag to the list of tags. That is why we check to see if the tag is present; if it is, we return the tag enclosed in <>. If the tag is not present, we return a paragraph tag as a default.

Now, let's look at ClosingTag:

public ClosingTag(tagType : TagType) : string {
let tag = this.tagType.get(tagType);
if (tag !== null) {
return `</${tag}>`;
}
return `</p>`;
}

Looking at these two methods, we can see that they are almost identical. When we think about the problem of creating our HTML tag, we realize that the only difference between an opening and a closing tag is that the closing tag has a / in it. With that in mind, we can change the code to use a helper method that accepts whether the tag starts with < or </:

private GetTag(tagType : TagType, openingTagPattern : string) : string {
let tag = this.tagType.get(tagType);
if (tag !== null) {
return `${openingTagPattern}${tag}>`;
}
return `${openingTagPattern}p>`;
}

All that remains is for us to add methods to retrieve the opening and closing tags:

public OpeningTag(tagType : TagType) : string {
return this.GetTag(tagType, `<`);
}

public ClosingTag(tagType : TagType) : string {
return this.GetTag(tagType, `</`);
}

Pulling this all together, the code for our TagTypeToHtml class now looks like this:

class TagTypeToHtml {
private readonly tagType : Map<TagType, string> = new Map<TagType, string>();
constructor() {
this.tagType.set(TagType.Header1, "h1");
this.tagType.set(TagType.Header2, "h2");
this.tagType.set(TagType.Header3, "h3");
this.tagType.set(TagType.Paragraph, "p");
this.tagType.set(TagType.HorizontalRule, "hr")
}

public OpeningTag(tagType : TagType) : string {
return this.GetTag(tagType, `<`);
}

public ClosingTag(tagType : TagType) : string {
return this.GetTag(tagType, `</`);
}

private GetTag(tagType : TagType, openingTagPattern : string) : string {
let tag = this.tagType.get(tagType);
if (tag !== null) {
return `${openingTagPattern}${tag}>`;
}
return `${openingTagPattern}p>`;
}
}
The single responsibility of our TagTypeToHtml class is mapping tagType to an HTML tag. Something that we are going to keep coming back to throughout this chapter is that we want classes to have a single responsibility. In OO theory, this is known as one of the principles of SOLID (short for Single Responsibility PrincipleOpen/Closed PrincipleLiskov Substitution PrincipleInterface Segregation PrincipleDependency Inversion Principle) design. The acronym refers to a set of complementary development techniques to create more robust code.
This handy acronym serves to guide us on how to structure classes and the most important part, in my opinion, is the Single Responsibility Principle, which states that a class should do one thing and one thing only. While I would certainly recommend reading about this topic (and we will touch on other aspects of it as we progress), in my opinion, the most important part of SOLID design is that classes are responsible for one thing and one thing only; everything else flows out of that principle. Classes that only do one thing are generally much easier to test and they are a lot easier to understand. That does not mean that they should only have one method. They can have many methods, as long as they are all related to the purpose of the class. We will cover this topic again and again throughout the book because it is so important.

主站蜘蛛池模板: 宁南县| 宝应县| 北流市| 新巴尔虎左旗| 繁峙县| 荣昌县| 忻州市| 博爱县| 马尔康县| 绥化市| 衢州市| 唐河县| 西青区| 永善县| 遂平县| 兴化市| 虎林市| 黎川县| 澄江县| 波密县| 长垣县| 平塘县| 安国市| 新建县| 广昌县| 大化| 石棉县| 越西县| 潞城市| 松桃| 绍兴市| 涿州市| 和平区| 特克斯县| 霍林郭勒市| 逊克县| 陇川县| 古田县| 海晏县| 武冈市| 新源县|