> 2021年11月12日信息消化 ### Daily Coding Problem This problem was asked by PayPal. Given a string and a number of lines k, print the string in zigzag form. In zigzag, characters are printed out diagonally from top left to bottom right until reaching the kth line, then back up to top right, and so on. For example, given the sentence "thisisazigzag" and k = 4, you should print: ```plain text t a g h s z a i i i z s g ``` ### Practical SOLID in Golang: Single Responsibility Principle > MEMO > One code structure must have only one reason to exist > 一个函数/结构只实现一个目的(用interface去分发给不同的结构)。 > e.g. > extractUsername: extractRawToken(get Authorization) > extractClaims(Parser get token) > return name [Practical SOLID in Golang: Single Responsibility Principle](https://levelup.gitconnected.com/practical-solid-in-golang-single-responsibility-principle-20afb8643483) > The Single Responsibility Principle (SRP) states that each software module should have one and only one reason to change. The sentence from above is the one written by Uncle Bob himself. Its meaning is initially bound to a module and dividing responsibilities by mapping them to the organization's day-to-day work. Today, SRP has a wide range, where it touches different aspects of the software. We can use its purpose in classes, functions, modules. And, naturally, in Go, we can use it in a struct. ```go type EmailService struct { db *gorm.DB smtpHost string smtpPassword string smtpPort int } func NewEmailService(db *gorm.DB, smtpHost string, smtpPassword string, smtpPort int) *EmailService { return &EmailService{ db: db, smtpHost: smtpHost, smtpPassword: smtpPassword, smtpPort: smtpPort, } } func (s *EmailService) Send(from string, to string, subject string, message string) error { email := EmailGorm{ From: from, To: to, Subject: subject, Message: message, } err := s.db.Create(&email).Error if err != nil { log.Println(err) return err } auth := smtp.PlainAuth("", from, s.smtpPassword, s.smtpHost) server := fmt.Sprintf("%s:%d", s.smtpHost, s.smtpPort) err = smtp.SendMail(server, auth, from, []string{to}, []byte(message)) if err != nil { log.Println(err) return err } return nil } ``` Let us examine the code block from above. There we have one struct, `EmailService`, with only one method, `Send`. We use this service for sending emails. Even if it looks fine, we realize that this code breaks every aspect of SRP when we scratch the surface. The responsibility of `EmailService` is not just to send emails but to store an email message into DB and send it via SMTP protocol. Take a closer look at the sentence above. The word "and" is bold with purpose. Using such an expression does not look like the case where we describe a single responsibility. > As soon as describing the responsibility of some code struct requires the usage of the word "and", it already breaks the Single Responsibility Principle. In our example, we broke SRP on many code levels. The first one is on the level of function. Function `Send` is responsible for both storing a message in the database and sending email over SMTP protocol. The second level is a struct `EmailService`. As we already concluded, it also has two responsibilities, **storing inside the database** and **sending emails**. What are the consequences of such a code? 1. When we change a table structure or type of storage, we need to change a code for sending emails over SMTP. 2. When we want to integrate Mailgun or Mailjet, we need to change a code for storing data in the MySQL database. 3. If we choose different integration of sending emails in the application, each integration needs to have a logic to store data in the database. 4. Suppose we decide to split the application's responsibility into two teams, one for maintaining a database and the second one for integrating email providers. In that case, they will work on the same code. 5. This service is practically untestable with unit tests. … So, let us refactor this code. #### How we do respect Single Responsibility **To split the responsibilities** in this case and make code blocks that have just one reason to exist, we should define a struct for each of them. It practically means to have a separate struct for storing data in some storage and a **different struct** for sending emails by using some integration with email providers. More in the code block below: ```go type EmailGorm struct { gorm.Model From string To string Subject string Message string } type EmailRepository interface { Save(from string, to string, subject string, message string) error } type EmailDBRepository struct { db *gorm.DB } func NewEmailRepository(db *gorm.DB) EmailRepository { return &EmailDBRepository{ db: db, } } func (r *EmailDBRepository) Save(from string, to string, subject string, message string) error { email := EmailGorm{ From: from, To: to, Subject: subject, Message: message, } err := r.db.Create(&email).Error if err != nil { log.Println(err) return err } return nil } type EmailSender interface { Send(from string, to string, subject string, message string) error } type EmailSMTPSender struct { smtpHost string smtpPassword string smtpPort int } func NewEmailSender(smtpHost string, smtpPassword string, smtpPort int) EmailSender { return &EmailSMTPSender{ smtpHost: smtpHost, smtpPassword: smtpPassword, smtpPort: smtpPort, } } func (s *EmailSMTPSender) Send(from string, to string, subject string, message string) error { auth := smtp.PlainAuth("", from, s.smtpPassword, s.smtpHost) server := fmt.Sprintf("%s:%d", s.smtpHost, s.smtpPort) err := smtp.SendMail(server, auth, from, []string{to}, []byte(message)) if err != nil { log.Println(err) return err } return nil } type EmailService struct { repository EmailRepository sender EmailSender } func NewEmailService(repository EmailRepository, sender EmailSender) *EmailService { return &EmailService{ repository: repository, sender: sender, } } func (s *EmailService) Send(from string, to string, subject string, message string) error { err := s.repository.Save(from, to, subject, message) if err != nil { return err } return s.sender.Send(from, to, subject, message) } ``` Here we provide two new structs. The first one is `EmailDBRepository` as an implementation for the `EmailRepository` interface. It includes support for **persisting data** in the underlying database. The second structure is `EmailSMTPSender` that implements the `EmailSender` interface. This struct is responsible for only email sending over SMPT protocol. Finally, the new `EmailService` contains interfaces from above and delegates the request for email sending. A question may appear: does `EmailService` still have multiple responsibilities, as it still holds a logic for storing and sending emails? Does it look like we just made an abstraction, but the duties are still there? Here, that is not the case. `EmailService` does not hold the responsibility for storing and sending emails. **It delegates them to the structs below**. Its responsibility is to delegate requests for processing emails to the underlying services. > There is a difference between holding and delegating responsibility. If an adaptation of a particular code can remove the whole purpose of responsibility, we talk about holding. If that responsibility still exists even after removing a specific code, then we talk about delegation. #### Conclusion The Single Responsibility Principle is the first one from SOLID principles. It represents a letter S in the word SOLID. It claims that **one code structure must have only one reason to exist**. We see those reasons as responsibilities. A structure can hold responsibility or delegate one. Whenever our structure contains multiple responsibilities, we should refactor that piece of code. ### How My Genius Roommate Changed My Perspective Origin: [How My Genius Roommate Changed My Perspective](https://www.farhadg.com/authors/farhad-ghayour) Roosevelt’s normative stance on making the best with what you have captured his attitude. > “Do what you can, with what you have, where you are.” ~Theodore Roosevelt Our apartment was in a new building still under construction. Our place was special in that it brought us together as close friends. I’ll omit our challenges with a non-functioning water heater and unstable electricity. It’s interesting to reflect back and realize we were practicing the [Wim Hof method](https://www.wimhofmethod.com/) without knowing about it. **I always kept my room simple and minimal. I only had the bare essentials.** I quickly came to realize that the term “essential” is relative with a wide spectrum—and mine was too bare for most. At the time, I used to sit on the floor, regardless of whether I was working, studying, or relaxin’ with friends. One day, my roommate decided to improve my appreciation for this way of working. “Improve it? It’s the floor, and I’m sitting on it. What do you have in mind?” I ask. He asks whether I’d like my bed extended. He explained that the ledges from an extended bed frame could serve as a working bench. It was attractive as it would also help my posture. I followed with questions about materials and how I could help. He responded that we could save our money and use what we have. “Use what we have?” I pondered to myself. How could we make the bed larger given the bed’s limited materials? It didn’t make a ton of sense but I was open. With a frantic mindset and a fast-paced Indonesian accent, he asked, “Ready? Just help me!” And with that, we began tearing my bed apart… My bed stood on a typical wooden-block foundation that supported the mattress. He then started to **disassemble that foundation**. He **repurposed some of the unnecessary parts to build an entirely new section**. It was amazing to see the entire process unfolding in front of me. There were no formal measurements and we didn’t buy any materials. He simply repurposed some of the original materials towards a new component. Here are two pictures of my bed with a retractable bench. When I pulled out the bench, I used it to work on my laptop, eat, or store my water jugs. But I was new to this way of thinking and I really didn’t have any particular goals or pain points. More importantly, our place was bare without any superfluous items. Most of the rooms comprised a simple computer desk, wardrobe, and bed. Nothing more; nothing less. After looking around for several minutes, I didn’t come up with anything else. Everything was being used for its intended purpose. This exercise inspired me to approach the world with this new-found mental tool. I thought to myself: “Identify my goal, take into account what I have, and creatively repurpose towards my goal.” I thought that I was ready. I had unlocked his way of thinking. I looked around my room and tried to find other things I could repurpose in creative ways. **It’s interesting to see how limited we are by our experiences, expectations, and mindsets.** > “The only thing that interferes with my learning is my education.” ~Albert Einstein While the term “genius” was polarizing for some folks, I found [`geocrasher`’s insight](https://news.ycombinator.com/item?id=29164495#29185460) that “ingenious” as more fitting to be compelling and insightful: - “ingenious: having or showing an unusual aptitude for discovering, inventing, or contriving an ingenious detective. 2 : marked by originality, resourcefulness, and cleverness in conception or execution an ingenious contraption.” ### Misc - THOMPSON: "...there is a paradox to scale, I think. People who want to be big sometimes think, "I have to immediately reach the largest possible audience." But in a weird way, the best way to produce things that take off is to produce small things. To become a small expert. To become the best person on the internet at understanding the application of Medicaid to minority children, or something like that. - [Step Indenting Your Code](https://www.youtube.com/watch?v=JnFh2NoAM4s) ```javascript if (a==b) return console.log(yes) ``` - [Structured thinking](https://twitter.com/warikoo/status/1456500811404439557?s=20) - **Writing**: What is needed, is a way to pace our thoughts. That is where writing helps. - **Getting rejected**. We all hate rejection. We fear it, in fact. We fear it so much, that we do not even start anything, for the fear of being rejected. Rejection though, is like a muscle. That can be built. For us to get "comfortable" with it. - We are the outcome of the habits we keep, the thoughts we store, the lesson we chose to learn. And the pain we agree to endure.