JPush.NET (JPush .NET Server SDK)

Project Description
JPush.NET is .NET Server SDK for JPush. It can help you send, monitor and manage all your JPush messages by programming, following JPush official REST API.

 

Sample for class JPushClientV3: (使用 类JPushClientV3的示例)

Here is a sample based on .NET console application.(这是一个基于.NET控制台应用程序的示例)

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using ifunction.JPush.V3;
using Newtonsoft.Json;
namespace ifunction.JPush.Test
{
    class Program
    {
        static void Main(string[] args)
        {
            var appKey = "1234567890abcdef"; // Your App Key from JPush
            var masterSecret = "1234567890abcdef"; // Your Master Secret from JPush

            Dictionary<string, string> customizedValues = new Dictionary<string, string>();
            customizedValues.Add("CK1", "CV1");
            customizedValues.Add("CK2", "CV2");
  
            JPushClientV3 client = new JPushClientV3(appKey, masterSecret);
            Audience audience = new Audience();
            // In JPush V3, tag can be multiple added with different values.
            // In following code, it is to send push to those who are in ((Tag1 AND Tag2) AND (Tag3 OR Tag4))
            // If you want to send to all, please use: audience.Add(PushTypeV3.Broadcast, null);
            audience.Add(PushTypeV3.ByTagWithinAnd, new List<string>(new string[] { "Tag1", "Tag2" }));
            audience.Add(PushTypeV3.ByTagWithinOr, new List<string>(new string[] { "Tag3", "Tag4" }));
  
            // In JPush V3, Notification would not be display on screen, it would be transferred to app instead.
            // And different platform can provide different notification data.
            Notification notification = new Notification
            {
                AndroidNotification = new AndroidNotificationParameters
                {
                    Title = "JPush provides V3.",
                    Alert = "JPush V2 would be retired soon.",
                    CustomizedValues = customizedValues
                },
                iOSNotification = new iOSNotificationParameters
                {
                    Badge = 1,
                    Alert = "JPush V2 would be retired soon.",
                    Sound = "YourSound",
                    CustomizedValues = customizedValues
                }
            };
  
            var response = client.SendPushMessage(new PushMessageRequestV3
            {
                Audience = audience,
                Platform = PushPlatform.AndroidAndiOS,
                IsTestEnvironment = true,
                AppMessage = new AppMessage
                {
                    Content = "Hello, this is a test push of V3 from .NET. Have a nice day!",
                    CustomizedValue = customizedValues
                },
                Notification = notification
            });
  
            Console.WriteLine(response.ResponseCode.ToString() + ":" + response.ResponseMessage);
            Console.WriteLine("Push sent.");
            Console.WriteLine(response.ResponseCode.ToString() + ":" + response.ResponseMessage);
  
            List<string> idToCheck = new List<string>();
            idToCheck.Add(response.MessageId);
  
            var statusList = client.QueryPushMessageStatus(idToCheck);
  
            Console.WriteLine("Status track is completed.");
            if (statusList != null)
            {
                foreach (var one in statusList)
                {
                    Console.WriteLine(string.Format("Id: {0}, Android: {1}, iOS: {2}", one.MessageId, one.AndroidDeliveredCount, one.ApplePushNotificationDeliveredCount));
                }
            }
  
            Console.WriteLine("Press any key to exit.");
            Console.Read();
        }
    }
}

RESTful API reference: http://docs.jpush.cn/display/dev/Push-API-v3

IMPORTANT NOTE(重要通告):
If you already use JPushClient V2 (Class: JPushClient), you can continue to use that. SDK Library would convert the old PushMessageRequest to PushMessageRequestV3 and send request based on JPush V3.
(如果你已经使用JPushClient V2,即SDK中的类 JPushClient,你可以继续使用它。SDK类库将在内部自动将PushMessageRequest转换到PushMessageRequestV3,并以V3形式访问JPush服务.)

Sample for JPushClient:

Here is a sample based on .NET console application.

class Program
{
      static void Main(string[] args)
      {
          var appKey = "1234567890abcdef"; // Your App Key from JPush
          var masterSecret = "1234567890abcdef"; // Your Master Secret from JPush
          
          Dictionary<string, string> customizedValues = new Dictionary<string, string>();
          customizedValues.Add("CK1", "CV1");
          customizedValues.Add("CK2", "CV2");
          JPushClient client = new JPushClient(appKey, masterSecret, false);
          
          var response = client.SendPushMessage(new PushMessageRequest
          {
              MessageType = MessageType.Notification,
              Platform = PushPlatform.Android,
              Description = "DotNET",
              PushType = PushType.Broadcast,
              IsTestEnvironment = true,
              Message = new PushMessage
              {
                  Content = "Hello, this is a test push from .NET. Have a nice day!",
                  PushTitle = "A title.",
                  Sound = "YourSound",
                  CustomizedValue = customizedValues
              }
          });
          
          Console.WriteLine(response.ResponseCode.ToString() + ":" + response.ResponseMessage);
          Console.WriteLine("Push sent.");
          Console.WriteLine(response.ResponseCode.ToString() + ":" + response.ResponseMessage);
          
          List<string> idToCheck = new List<string>();
          idToCheck.Add(response.MessageId);
          
          var statusList = client.QueryPushMessageStatus(idToCheck);
          Console.WriteLine("Status track is completed.");
          
          if (statusList != null)
          {
              foreach (var one in statusList)
              {
                  Console.WriteLine(string.Format("Id: {0}, Android: {1}, iOS: {2}", one.MessageId, one.AndroidDeliveredCount, one.ApplePushNotificationDeliveredCount));
              }
          }
          
          Console.WriteLine("Press any key to exit.");
          Console.Read();
      }
}

RESTful API reference: http://docs.jpush.cn/display/dev/Index

 

LINK: http://jpush.codeplex.com/

40 bài học cuộc sống cần cho mọi lứa tuổi

Regina Brett viết những bài học cuộc sống này vào đêm trước ngày sinh nhật lần thứ 45 của cô sau khi được chẩn đoán ung thư vú.

Những đúc kết đã “chạm” đến tâm trí người đọc bất kể tuổi tác.

1. Cuộc sống vốn không công bằng, nhưng nó vẫn còn tốt chán.

2. Dừng những nghi ngờ, hãy thực hiện từng bước nhỏ trong kế hoạch dài bạn đã vạch ra.

3. Cuộc đời quá ngắn để lãng phí thời gian ghét bỏ ai đó.

4. Bản thân đừng quá lo lắng hay nghiêm trọng hóa vấn đề, chẳng ai quan tâm như bạn lo sợ đâu.

happy-beach-2-7696-1413864682.jpg

Tận hưởng cuộc sống đến từng phút giây, từng hơi thở. Ảnh: Business Insider.

5. Trả hết thẻ tín dụng của bạn mỗi tháng.

6. Bạn không cần phải thắng trong mọi lý luận, tranh cãi. Chấp nhận thua trong một cuộc lý luận chẳng chết ai cả, đôi khi lại cứu một mối quan hệ.

7. Hãy khóc với một ai đó, điều này giúp hồi phục nhanh hơn là khóc một mình.

8. Tiết kiệm cho khoảng thời gian hưu trí ngay từ tháng lương đầu tiên bạn nhận được, điều này không sớm lắm đâu.

9. Khi nói đến chocolate, sự kháng cự là vô ích. Thỉnh thoảng hãy tự thưởng một thanh kẹo ngọt ngào, đọc một cuốn sách trong căn phòng ấm khi bên ngoài trời đang mưa. Tại sao không?

10. Hãy làm hòa với quá khứ của bạn, đừng để nó in dấu lên cuộc sống hiện tại và tương lai. Bạn khó mà tưởng tượng được tác động của sự nuối tiếc quá khứ sẽ làm khổ sở và cản đường bạn đến thế nào. Chuyện gì của quá khứ hãy để nó ngủ yên.

11. Sẽ ổn thôi nếu bạn để những đứa con thấy bạn khóc. Ai cũng có những phút yếu lòng.

12. Đừng so sánh cuộc sống của bạn với bất cứ ai. Bạn không biết tất cả cuộc hành trình và những cái giá của họ đâu.

13. Nếu mối quan hệ có một bí mật không thể tiết lộ, hãy xem xét lại thật thấu đáo.

14. Cuộc sống quá ngắn cho các mối quan hệ chỉ dựa vào sự thương hại, hãy đối đáp với nhau bằng tấm chân tình.

15. Bạn luôn có thể có bất cứ thứ gì mình muốn trong tương lai, thông qua hành động của hôm nay. Muốn đi du học, hãy trau dồi ngoại ngữ từ hôm nay.

woman-reading-outside-studying-1230-4176

Môi trường bên ngoài luôn tuyệt vời, đừng suốt ngày ở trong phòng, ngồi với chiếc máy tính. Ảnh: Business Insider.

16. Công việc của một nhà văn là viết. Nếu bạn muốn trở thành nhà văn, không cách nào khác là phải viết.

17. Không bao giờ là quá muộn để có cuộc sống hạnh phúc, điều này hoàn toàn tùy thuộc vào bản thân bạn.

18. Thắp sáng những ngọn nến cho căn phòng lãng mạn, sử dụng các tờ giấy đẹp, mặc đồ lót ưa thích. Không dành nó cho một dịp đặc biệt nào. Ngày hôm nay chính là một ngày đặc biệt.

19. Chuẩn bị mọi thứ chu đáo nhất có thể, sau đó theo dòng chảy.

20. Bộ phận sinh dục quan trọng nhất là não.

21. Không một ai chịu trách nhiệm về hạnh phúc của bạn ngoài chính bạn.

22. Tha thứ cho tất cả mọi người, tất cả mọi thứ.

23. Những gì người khác nghĩ về bạn không phải là việc của bạn.

24. Thời gian chữa lành hầu hết mọi vết thương.

25. Dù tình huống hiện tại có tốt hay tệ, hãy yên tâm rồi nó sẽ thay đổi.

26. Công việc của bạn sẽ không chăm sóc bạn khi ốm đau. Những người bạn sẽ làm việc này, giữ liên lạc và quan tâm đến họ.

27.  Tin vào phép lạ.

28. Những điều không thể hạ gục bạn sẽ làm bạn mạnh mẽ hơn.

29. Những đứa con của bạn chỉ có một thời thơ ấu, hãy làm cho thời thơ ấu duy nhất ấy thật đáng nhớ.

30. Hãy ra ngoài mỗi ngày, phép lạ đang chờ đợi bạn ở mọi nơi.

31. Đừng kiểm toán hay kiểm kê cuộc sống, bắt lấy nó và sống trọn vẹn từng phút giây của hiện tại.

32. Loại bỏ tất cả những gì không lợi ích, không đẹp và không vui vẻ. Đây là cuộc sống của bạn kia mà.

33. Tất cả những gì thực sự quan trọng sau cùng là tình yêu thương.

34. Điều tốt nhất vẫn chưa đến.

35. Nếu không yêu cầu, bạn sẽ chẳng bao giờ nhận được.

36. Làm việc năng suất chứ đừng làm việc kéo dài thời gian trì trệ để chờ cuối tháng nhận lương.

37. Hãy hít thở sâu, nó làm dịu tâm trí bạn đến không ngờ.

38. Trao đổi các vấn đề với nhau, mọi người cũng có những vấn đề của họ, hãy giúp họ nhìn ra vì bạn là người đứng bên ngoài, dễ nhận thấy. Rồi tương tự họ sẽ giúp bạn nhận ra vấn đề của mình.

39. Những cơ hội mới, những người bạn, người yêu tiềm năng luôn ở bên ngoài vòng an toàn của bạn.

40.  Cuộc sống này có những ràng buộc đôi khi khó chịu, nhưng mỗi ngày sống vẫn là một món quà kỳ diệu.

Khánh Ly (Theo Business Insider)

Ý kiến bạn đọc (60)

Để có cuộc sống tốt đẹp hơn, mời các bạn tham khảo 4 quy tắc sống của người Ấn Độ:

1. Quy tắc đầu tiên: “Bất cứ người nào bạn gặp cũng đúng là người mà bạn cần gặp”

Điều này có nghĩa rằng không ai xuất hiện trong cuộc đời chúng ta một cách tình cờ. Mỗi người xung quanh chúng ta, bất cứ ai chúng ta giao lưu, gặp gỡ đều đại diện cho một điều gì đó, có thể là để dạy chúng ta điều gì đó hoặc giúp chúng ta cải thiện tình hình hiện tại. Vì vậy, hãy tôn trọng và coi trọng những người mà chúng ta gặp gỡ!

2. Quy tắc thứ hai: “Bất cứ điều gì xảy thì đó chính là điều nên xảy ra”

Không có điều gì tuyệt đối, không có điều gì chúng ta trải nghiệm lại nên khác đi cả. Thậm chí cả với những điều nhỏ nhặt ít quan trọng nhất.

Không có: “Nếu như tôi đã làm điều đó khác đi…, thì nó hẳn đã khác đi. “

Những gì đã xảy ra chính là những gì nên xảy ra và phải xảy ra giúp chúng ta có thêm bài học để tiến về phía trước. Bất kỳ tình huống nào trong cuộc đời mà chúng ta đối mặt đều tuyệt đối hoàn hảo, thậm chí cả khi nó thách thức sự hiểu biết và bản ngã của chúng ta.

3. Quy tắc thứ ba: “Trong mỗi khoảnh khắc, mọi sự đều bắt đầu vào đúng thời điểm”

Mọi thứ bắt đầu vào đúng thời điểm, không sớm hơn hay muộn hơn.
Khi chúng ta sẵn sàng cho nó, cho điều gì đó mới mẻ trong cuộc đời mình, thì nó sẽ có đó, sẵn sàng để bắt đầu.

4. Quy tắc thứ tư: “Những gì đã qua, cho qua”

Quy tắc này rất đơn giản. Khi điều gì đó trong cuộc sống của chúng ta kết thúc, thì có nghĩa là nó đã giúp ích xong cho sự tiến hoá của chúng ta. Đó là lý do tại sao, để làm phong phú thêm trải nghiệm của mình, tốt hơn hết là chúng ta hãy buông bỏ và tiếp tục cuộc hành trình.

Tôi nghĩ là không phải là tình cờ mà bạn đang đọc những dòng này.Nếu bài viết này đánh động tâm hồn bạn, đó là bởi vì bạn đáp ứng được những yêu cầu và hiểu rằng không một bông tuyết nào lại tình cờ rơi xuống sai chỗ cả. 

http://doisong.vnexpress.net/tin-tuc/gia-dinh/40-bai-hoc-cuoc-song-can-cho-moi-lua-tuoi-3096280.html

Hướng dẫn sử dụng đồng hồ đeo tay (Một số lưu ý đặc biệt khác)

– Không nên thử độ cứng và chống xước của kính đồng hồ (Sapphire glass) bằng các vật có tính chất cứng hơn Sapphire như dao cắt kính, kim cương… vì như vậy sẽ làm hư hại kính đồng hồ.- Không sử dụng hoặc để đồng hồ ở nơi có nhiều từ trường như TV, Tủ lạnh, Máy tính, Lò vi sóng vv…

– Luôn rửa đồng hồ bằng nước ấm (chú ý không dùng nước nóng bốc hơi tại vòi) ngay sau khi bơi biển (đối với đồng hồ được phép bơi lặn)

– Luôn kiểm tra tình trạng của núm vặn, vị trí đúng là ở nấc trong cùng. Trong quá trình sử dụng núm rất dễ bị mắc vào chỉ áo hoặc những tác động khác mà bị kéo ra ngoài hoặc sau khi chỉnh lại Ngày/Giờ, để tránh nước có thể thẩm thấu vào bên trong đồng hồ.

– Hàng tuần nên chùi rửa đồng hồ với nước ấm với xà-phòng để chải sạch bụi bẩn và muối đọng do mồ hôi tiết ra. Những bụi bẩn và mồ hôi muối chính là tác nhân gây ra nước vào trong đồng hồ.

– Không để đồng hồ ở nơi có nhiệt độ cao quá 60 độ C (tương đương 140 độ F) hoặc những nơi thấp hơn 0 độ C (tương đương 32 độ F).

– Không sử dụng các nút bấm khi ở dưới nước đối với những đồng hồ nhiều chức năng.

– Không nên đeo đồng hồ khi tắm nước nóng hoặc xông hơi vì sẽ làm suy giảm khả năng chống thấm nước của đồng hồ.

– Không nên đeo đồng hồ khi làm các công việc nặng, công việc có nhiều động tác va chạm với các vật dụng bên ngoài như: sửa chữa máy móc, khuân vác… vì vỏ đồng hồ, kính, dây đeo dễ bị trầy xước hư hại.

– Bộ máy của đồng hồ được chế tạo rất tinh vi, với các bộ phận nhỏ xíu kết hợp với nhau và để đồng hồ hoạt động ổn định, bền lâu không nên đeo đồng hồ khi tham gia các hoạt động vận động mạnh như: Bơi lội, Bóng đá, Bóng chuyền, Quần vợt (Tennis)…ngoại trừ đồng hồ chuyên dụng dành riêng cho thể thao.
– Luôn đảm bảo có đủ năng lượng cho bộ máy của đồng hồ, thay Pin định kỳ và đúng chủng loại Pin tại đại lý ủy quyền chính hãng để chiếc đồng hồ của bạn luôn chạy chính xác, giữ được hình thức đẹp lâu bền.

– Cũng cần lưu ý rằng việc thường xuyên để đồng hồ tiếp xúc với hóa chất: (Xà phòng, nước biển, các chất tẩy rửa, axít) có thể làm hỏng dây đồng hồ, các lớp vỏ mạ, vòng tay. Mỹ phẩm và nước hoa cũng có thể làm hỏng đồng hồ nếu để tiếp xúc trực tiếp với các mặt đồng hồ mạ và dây đeo.

Hướng dẫn sử dụng đồng hồ dây kim loại

– Đối với đồng hồ dây kim loại trong quá trình sử dụng bị mồ hôi, ghét, bụi làm cáu bẩn có thể sử dụng Kem đánh răng hoặc Nước rửa tay để làm sạch.
– Dùng bàn chải đánh răng cọ rửa nhẹ nhàng sau đó rửa lại kỹ bằng nước sạch, lau khô bằng khăn mềm.
– Điều này sẽ gia tăng tuổi thọ của đồng hồ.
– Không sử dụng bất kì dung môi, chất làm sạch, chất tẩy công nghiệp, chất dính, sơn hoặc các chất xịt lên bề mặt đồng hồ. (Đóng chặt núm điều chỉnh để trách bị nước thẩm thấu vào đồng hồ trước khi cọ rửa).

Hướng dẫn đồng hồ sử dụng máy tự động (Automatic movement)

– Đối với các đồng hồ sử dụng bộ máy tự động (Automatic movement) hoặc lên dây (Handwinding movement), thì việc chỉnh giờ,
lịch hay lên dây tránh thực hiện trong khoảng thời gian từ 21h00 – 03h00 hàng ngày. Bởi nếu thực hiện các thao tác trên trong khoảng thời gian này sẽ gây hư hại ngoài ý muốn cho hệ thống bánh xe của đồng hồ.
– Giống như Xe hơi, đồng hồ Automatic cũng cần được bảo dưỡng thường xuyên để các bộ phận nhỏ xíu, các chi tiết tinh vi kết hợp với nhau tốt nhất giúp đồng hồ hoạt động luôn chính xác và tăng thêm tuổi thọ của đồng hồ. Định kỳ hằng năm hãy mang đồng hồ Automatic của bạn tới Đại lý ủy quyền chính hãng để kỹ thuật viên chuyên nghiệp về đồng hồ giúp bạn lau dầu và bảo dưỡng.
– Vào ban đêm, khi tháo đồng hồ ra, bạn hãy đặt úp mặt của đồng hồ xuống, nhớ là lót ở dưới bằng một miếng vải hoặc giấy mềm để đồng hồ không bị xước. Khi mặt của đồng hồ úp xuống các bộ phận máy của đồng hồ chia đều lực và không đè lên nhau giảm lực ma sát giữa các linh kiện bên trong đồng hồ, giúp kéo dài tuổi thọ của đồng hồ.

SOLID Principles in C#

Hi everybody. In this article I will cover the following topics.

  1. Reasons behind most of the unsuccessful application
  2. Solutions
  3. Intro to SOLID principles
  4. SRP
  5. OPCP
  6. LSP
  7. ISP
  8. DIP

Reason behind most of the unsuccessful application

Developers start building applications with good and tidy designs using their knowledge and experience. But over time, applications might develop bugs. The application design must be altered for every change request or new feature request. After some time we might need to put in a lot of effort, even for simple tasks and it might require a full working knowledge of the entire system. But we can’t blame change or new feature requests. They are part of the software development. We can’t stop them and refuse them either. So who is the culprit here? Obviously it is the design of the application.

The following are the design flaws that cause the damage in software, mostly.

  1. Putting more stress on classes by assigning more responsibilities to them. (A lot of functionality not related to a class.)
  2. Forcing the classes to depend on each other. If classes are dependent on each other (in other words tightly coupled), then a change in one will affect the other.
  3. Spreading duplicate code in the system/application.

Solution

  1. Choosing the correct architecture (in other words MVC, 3-tier, Layered, MVP, MVVP and so on).
  2. Following Design Principles.
  3. Choosing correct Design Patterns to build the software based on it’s specifications.

Now we go through the Design Principles first and will cover the rest soon.

Intro to SOLID principles

SOLID principles are the design principles that enable us to manage with most of the software design problems. Robert C. Martin compiled these principles in the 1990s. These principles provide us ways to move from tightly coupled code and little encapsulation to the desired results of loosely coupled and encapsulated real needs of a business properly. SOLID is an acronym of the following.

  • S: Single Responsibility Principle (SRP)
  • O: Open closed Principle (OSP)
  • L: Liskov substitution Principle (LSP)
  • I: Interface Segregation Principle (ISP)
  • D: Dependency Inversion Principle (DIP)

S: Single Responsibility Principle (SRP)

SRP says “Every software module should have only one reason to change”.

This means that every class, or similar structure, in your code should have only one job to do. Everything in that class should be related to a single purpose. Our class should not be like a Swiss knife wherein if one of them needs to be changed then the entire tool needs to be altered. It does not mean that your classes should only contain one method or property. There may be many members as long as they relate to the single responsibility.

The Single Responsibility Principle gives us a good way of identifying classes at the design phase of an application and it makes you think of all the ways a class can change. A good separation of responsibilities is done only when the full picture of how the application should work. Let us check this with an example.

  1. public class UserService
  2. {
  3.    public void Register(string email, string password)
  4.    {
  5.       if (!ValidateEmail(email))
  6.          throw new ValidationException(“Email is not an email”);
  7.          var user = new User(email, password);
  8.          SendEmail(new MailMessage(“mysite@nowhere.com”, email) { Subject=“HEllo foo” });
  9.          public virtual bool ValidateEmail(string email)
  10.          {
  11.             return email.Contains(“@”);
  12.          }
  13.          public bool SendEmail(MailMessage message)
  14.          {
  15.             _smtpClient.Send(message);
  16.          }
  17.    }

It looks fine, but it is not following SRP. The SendEmail and ValidateEmail methods have nothing to do within the UserService class. Let’s refract it.

  1. public class UserService
  2. {
  3.    EmailService _emailService;
  4.    DbContext _dbContext;
  5.    public UserService(EmailService aEmailService, DbContext aDbContext)
  6.    {
  7.       _emailService = aEmailService;
  8.       _dbContext = aDbContext;
  9.    }
  10.    public void Register(string email, string password)
  11.    {
  12.       if (!_emailService.ValidateEmail(email))
  13.          throw new ValidationException(“Email is not an email”);
  14.          var user = new User(email, password);
  15.          _dbContext.Save(user);
  16.          emailService.SendEmail(new MailMessage(“myname@mydomain.com”, email) {Subject=“Hi. How are you!”});
  17.       }
  18.    }
  19.    public class EmailService
  20.    {
  21.       SmtpClient _smtpClient;
  22.    public EmailService(SmtpClient aSmtpClient)
  23.    {
  24.       _smtpClient = aSmtpClient;
  25.    }
  26.    public bool virtual ValidateEmail(string email)
  27.    {
  28.       return email.Contains(“@”);
  29.    }
  30.    public bool SendEmail(MailMessage message)
  31.    {
  32.       _smtpClient.Send(message);
  33.    }
  34. }

O: Open/Closed Principle

The Open/closed Principle says “A software module/class is open for extension and closed for modification”.

Here “Open for extension” means, we need to design our module/class in such a way that the new functionality can be added only when new requirements are generated. “Closed for modification” means we have already developed a class and it has gone through unit testing. We should then not alter it until we find bugs. As it says, a class should be open for extensions, we can use inheritance to do this. Okay, let’s dive into an example.

Suppose we have a Rectangle class with the properties Height and Width.

  1. public class Rectangle{
  2.    public double Height {get;set;}
  3.    public double Wight {get;set; }
  4. }

Our app needs the ability to calculate the total area of a collection of Rectangles. Since we already learned the Single Responsibility Principle (SRP), we don’t need to put the total area calculation code inside the rectangle. So here I created another class for area calculation.

  1. public class AreaCalculator {
  2.    public double TotalArea(Rectangle[] arrRectangles)
  3.    {
  4.       double area;
  5.       foreach(var objRectangle in arrRectangles)
  6.       {
  7.          area += objRectangle.Height * objRectangle.Width;
  8.       }
  9.       return area;
  10.    }
  11. }

Hey, we did it. We made our app without violating SRP. No issues for now. But can we extend our app so that it could calculate the area of not only Rectangles but also the area of Circles as well? Now we have an issue with the area calculation issue, because the way to do circle area calculation is different. Hmm. Not a big deal. We can change the TotalArea method a bit, so that it can accept an array of objects as an argument. We check the object type in the loop and do area calculation based on the object type.

  1. public class Rectangle{
  2.    public double Height {get;set;}
  3.    public double Wight {get;set; }
  4. }
  5. public class Circle{
  6.    public double Radius {get;set;}
  7. }
  8. public class AreaCalculator
  9. {
  10.    public double TotalArea(object[] arrObjects)
  11.    {
  12.       double area = 0;
  13.       Rectangle objRectangle;
  14.       Circle objCircle;
  15.       foreach(var obj in arrObjects)
  16.       {
  17.          if(obj is Rectangle)
  18.          {
  19.             objRectangle = (Rectangle)obj;
  20.             area += obj.Height * obj.Width;
  21.          }
  22.          else
  23.          {
  24.             objCircle = (Circle)obj;
  25.             area += objCircle.Radius * objCircle.Radius * Math.PI;
  26.          }
  27.       }
  28.       return area;
  29.    }
  30. }

Wow. We are done with the change. Here we successfully introduced Circle into our app. We can add a Triangle and calculate it’s area by adding one more “if” block in the TotalArea method of AreaCalculator. But every time we introduce a new shape we need to alter the TotalArea method. So the AreaCalculator class is not closed for modification. How can we make our design to avoid this situation? Generally we can do this by referring to abstractions for dependencies, such as interfaces or abstract classes, rather than using concrete classes. Such interfaces can be fixed once developed so the classes that depend upon them can rely upon unchanging abstractions. Functionality can be added by creating new classes that implement the interfaces. So let’s refract our code using an interface.

  1. public abstract class Shape
  2. {
  3.    public abstract double Area();
  4. }

Inheriting from Shape, the Rectangle and Circle classes now look like this:

  1. public class Rectangle: Shape
  2. {
  3.    public double Height {get;set;}
  4.    public double Width {get;set;}
  5.    public override double Area()
  6.    {
  7.       return Height * Width;
  8.    }
  9. }
  10. public class Circle: Shape
  11. {
  12.    public double Radius {get;set;}
  13.    public override double Area()
  14.    {
  15.       return Radius * Radus * Math.PI;
  16.    }
  17. }

Every shape contains its area show with it’s own way of calculation functionality and our AreaCalculator class will become simpler than before.

  1. public class AreaCalculator
  2. {
  3.    public double TotalArea(Shape[] arrShapes)
  4.    {
  5.       double area=0;
  6.       foreach(var objShape in arrShapes)
  7.       {
  8.          area += objShape.Area();
  9.       }
  10.       return area;
  11.    }
  12. }

Now our code is following SRP and OCP both. Whenever you introduce a new shape by deriving from the “Shape” abstract class, you need not change the “AreaCalculator” class. Awesome. Isn’t it?

L: Liskov substitution Principle

The Liskov Substitution Principle (LSP) states that “you should be able to use any derived class instead of a parent class and have it behave in the same manner without modification”. It ensures that a derived class does not affect the behavior of the parent class, in other words that a derived class must be substitutable for its base class.

This principle is just an extension of the Open Close Principle and it means that we must ensure that new derived classes extend the base classes without changing their behavior. I will explain this with a real world example that violates LSP.

A father is a doctor whereas his son wants to become a cricketer. So here the son can’t replace his father even though they both belong to the same family hierarchy.

Now jump into an example to learn how a design can violate LSP. Suppose we need to build an app to manage data using a group of SQL files text. Here we need to write functionality to load and save the text of a group of SQL files in the application directory. So we need a class that manages the load and save of the text of group of SQL files along with the SqlFile Class.

  1. public class SqlFile
  2. {
  3.    public string FilePath {get;set;}
  4.    public string FileText {get;set;}
  5.    public string LoadText()
  6.    {
  7.       /* Code to read text from sql file */
  8.    }
  9.    public string SaveText()
  10.    {
  11.       /* Code to save text into sql file */
  12.    }
  13. }
  14. public class SqlFileManager
  15. {
  16.    public List<SqlFile> lstSqlFiles {get;set}
  17.    public string GetTextFromFiles()
  18.    {
  19.       StringBuilder objStrBuilder = new StringBuilder();
  20.       foreach(var objFile in lstSqlFiles)
  21.       {
  22.          objStrBuilder.Append(objFile.LoadText());
  23.       }
  24.       return objStrBuilder.ToString();
  25.    }
  26.    public void SaveTextIntoFiles()
  27.    {
  28.       foreach(var objFile in lstSqlFiles)
  29.       {
  30.          objFile.SaveText();
  31.       }
  32.    }
  33. }

OK. We are done with our part. the functionality looks good for now. After some time our lead might tell us that we may have a few read-only files in the application folder, so we need to restrict the flow whenever it tries to do a save on them.

OK. We can do that by creating a “ReadOnlySqlFile” class that inherits the “SqlFile” class and we need to alter the SaveTextIntoFiles() method by introducing a condition to prevent calling the SaveText() method on ReadOnlySqlFile instances.

  1. public class SqlFile
  2. {
  3.    public string LoadText()
  4.    {
  5.    /* Code to read text from sql file */
  6.    }
  7.    public void SaveText()
  8.    {
  9.       /* Code to save text into sql file */
  10.    }
  11. }
  12. public class ReadOnlySqlFile: SqlFile
  13. {
  14.    public string FilePath {get;set;}
  15.    public string FileText {get;set;}
  16.    public string LoadText()
  17.    {
  18.       /* Code to read text from sql file */
  19.    }
  20.    public void SaveText()
  21.    {
  22.       /* Throw an exception when app flow tries to do save. */
  23.       throw new IOException(“Can’t Save”);
  24.    }
  25. }

To avoid an exception we need to modify “SqlFileManager” by adding one condition to the loop.

  1. public class SqlFileManager
  2. {
  3.    public List<SqlFile? lstSqlFiles {get;set}
  4.    public string GetTextFromFiles()
  5.    {
  6.       StringBuilder objStrBuilder = new StringBuilder();
  7.       foreach(var objFile in lstSqlFiles)
  8.       {
  9.          objStrBuilder.Append(objFile.LoadText());
  10.       }
  11.       return objStrBuilder.ToString();
  12.    }
  13.    public void SaveTextIntoFiles()
  14.    {
  15.       foreach(var objFile in lstSqlFiles)
  16.       {
  17.          //Check whether the current file object is read only or not.If yes, skip calling it’s
  18.          // SaveText() method to skip the exception.
  19.          if(! objFile is ReadOnlySqlFile)
  20.          objFile.SaveText();
  21.       }
  22.    }
  23. }

Here we altered the SaveTextIntoFiles() method in the SqlFileManager class to determine whether or not the instance is of ReadOnlySqlFile to avoid the exception. We can’t use this ReadOnlySqlFile class as a substitute of it’s parent without altering SqlFileManager code. So we can say that this design is not following LSP. Let’s make this design follow the LSP. Here we will introduce interfaces to make the SqlFileManager class independent from the rest of the blocks.

  1. public interface IReadableSqlFile
  2. {
  3.    string LoadText();
  4. }
  5. public interface IWritableSqlFile
  6. {
  7.    void SaveText();
  8. }

Now we implement IReadableSqlFile through the ReadOnlySqlFile class that reads only the text from read-only files.

  1. public class ReadOnlySqlFile: IReadableSqlFile
  2. {
  3.    public string FilePath {get;set;}
  4.    public string FileText {get;set;}
  5.    public string LoadText()
  6.    {
  7.       /* Code to read text from sql file */
  8.    }
  9. }

Here we implement both IWritableSqlFile and IReadableSqlFile in a SqlFile class by which we can read and write files.

  1. public class SqlFile: IWritableSqlFile,IReadableSqlFile
  2. {
  3.    public string FilePath {get;set;}
  4.    public string FileText {get;set;}
  5.    public string LoadText()
  6.    {
  7.       /* Code to read text from sql file */
  8.    }
  9.    public void SaveText()
  10.    {
  11.       /* Code to save text into sql file */
  12.    }
  13. }

Now the design of the SqlFileManager class becomes like this:

  1. public class SqlFileManager
  2. {
  3.    public string GetTextFromFiles(List<IReadableSqlFile> aLstReadableFiles)
  4.    {
  5.       StringBuilder objStrBuilder = new StringBuilder();
  6.       foreach(var objFile in aLstReadableFiles)
  7.       {
  8.          objStrBuilder.Append(objFile.LoadText());
  9.       }
  10.       return objStrBuilder.ToString();
  11.    }
  12.    public void SaveTextIntoFiles(List<IWritableSqlFile> aLstWritableFiles)
  13.    {
  14.    foreach(var objFile in aLstWritableFiles)
  15.    {
  16.       objFile.SaveText();
  17.    }
  18.    }
  19. }

Here the GetTextFromFiles() method gets only the list of instances of classes that implement the IReadOnlySqlFile interface. That means the SqlFile and ReadOnlySqlFile class instances. And the SaveTextIntoFiles() method gets only the list instances of the class that implements the IWritableSqlFiles interface, in other words SqlFile instances in this case. Now we can say our design is following the LSP. And we fixed the problem using the Interface segregation principle by (ISP) identifying the abstraction and the responsibility separation method.

I: Interface Segregation Principle (ISP)

The Interface Segregation Principle states “that clients should not be forced to implement interfaces they don’t use. Instead of one fat interface many small interfaces are preferred based on groups of methods, each one serving one sub module.”.

We can define it in another way. An interface should be more closly related to the code that uses it than code that implements it. So the methods on the interface are defined by which methods the client code needs than which methods the class implements. So clients should not be forced to depend upon interfaces that they don’t use.

Like classes, each interface should have a specific purpose/responsibility (refer to SRP). You shouldn’t be forced to implement an interface when your object doesn’t share that purpose. The larger the interface, the more likely it includes methods that not all implementers can do. That’s the essence of the Interface Segregation Principle. Let’s start with an example that breaks ISP. Suppose we need to build a system for an IT firm that contains roles like TeamLead and Programmer where TeamLead divides a huge task into smaller tasks and assigns them to his/her programmers or can directly work on them.

Based on specifications, we need to create an interface and a TeamLead class to implement it.

  1. public Interface ILead
  2. {
  3.    void CreateSubTask();
  4.    void AssginTask();
  5.    void WorkOnTask();
  6. }
  7. public class TeamLead : ILead
  8. {
  9.    public void AssignTask()
  10.    {
  11.       //Code to assign a task.
  12.    }
  13.    public void CreateSubTask()
  14.    {
  15.       //Code to create a sub task
  16.    }
  17.    public void WorkOnTask()
  18.    {
  19.       //Code to implement perform assigned task.
  20.    }
  21. }

OK. The design looks fine for now. Later another role like Manager, who assigns tasks to TeamLead and will not work on the tasks, is introduced into the system. Can we directly implement an ILead interface in the Manager class, like the following?

  1. public class Manager: ILead
  2. {
  3.    public void AssignTask()
  4.    {
  5.       //Code to assign a task.
  6.    }
  7.    public void CreateSubTask()
  8.    {
  9.       //Code to create a sub task.
  10.    }
  11.    public void WorkOnTask()
  12.    {
  13.       throw new Exception(“Manager can’t work on Task”);
  14.    }
  15. }

Since the Manager can’t work on a task and at the same time no one can assign tasks to the Manager, this WorkOnTask() should not be in the Manager class. But we are implementing this class from the ILead interface, we need to provide a concrete Method. Here we are forcing the Manager class to implement a WorkOnTask() method without a purpose. This is wrong. The design violates ISP. Let’s correct the design.

Since we have three roles, 1. Manager, that can only divide and assign the tasks , 2. TeamLead that can divide and assign the tasks and can work on them as well, 3. Programmer that can only work on tasks, we need to divide the responsibilities by segregating the ILead interface. An interface that provides a contract for WorkOnTask().

  1. public interface IProgrammer
  2. {
  3.    void WorkOnTask();
  4. }

An interface that provide contracts to manage the tasks:

  1. public interface ILead
  2. {
  3.    void AssignTask();
  4.    void CreateSubTask();
  5. }

Then the implementation becomes:

  1. public class Programmer: IProgrammer
  2. {
  3.    public void WorkOnTask()
  4.    {
  5.       //code to implement to work on the Task.
  6.    }
  7. }
  8. public class Manager: ILead
  9. {
  10.    public void AssignTask()
  11.    {
  12.       //Code to assign a Task
  13.    }
  14.    public void CreateSubTask()
  15.    {
  16.    //Code to create a sub taks from a task.
  17.    }
  18. }

TeamLead can manage tasks and can work on them if needed. Then the TeamLead class should implement both of the IProgrammer and ILead interfaces.

  1. public class TeamLead: IProgrammer, ILead
  2. {
  3.    public void AssignTask()
  4.    {
  5.       //Code to assign a Task
  6.    }
  7.    public void CreateSubTask()
  8.    {
  9.       //Code to create a sub task from a task.
  10.    }
  11.    public void WorkOnTask()
  12.    {
  13.       //code to implement to work on the Task.
  14.    }
  15. }

Wow. Here we separated responsibilities/purposes and distributed them on multiple interfaces and provided a good level of abstraction too.

D: Dependency Inversion Principle

The Dependency Inversion Principle (DIP) states that high-level modules/classes should not depend upon low-level modules/classes. Both should depend upon abstractions. Secondly, abstractions should not depend upon details. Details should depend upon abstractions.

High-level modules/classes implement business rules or logic in a system (application). Low-level modules/classes deal with more detailed operations, in other words they may deal with writing information to databases or passing messages to the operating system or services.

A high-level module/class that has dependency on low-level modules/classes or some other class and knows a lot about the other classes it interacts with is said to be tightly coupled. When a class knows explicitly about the design and implementation of another class, it raises the risk that changes to one class will break the other class. So we must keep these high-level and low-level modules/class loosely coupled as much as we can. To do that, we need to make both of them dependent on abstractions instead of knowing each other. Let’s start an with an example.

Suppose we need to work on an error logging module that logs exception stack traces into a file. Simple, isn’t it? The following are the classes that provide functionality to log a stack trace into a file.

  1. public class FileLogger
  2. {
  3.    public void LogMessage(string aStackTrace)
  4.    {
  5.       //code to log stack trace into a file.
  6.    }
  7. }
  8. public static class ExceptionLogger
  9. {
  10.    public static void LogIntoFile(Exception aException)
  11.    {
  12.       FileLogger objFileLogger = new FileLogger();
  13.       objFileLogger.LogMessage(GetUserReadableMessage(aException));
  14.    }
  15.    private string GetUserReadableMessage(Exception ex)
  16.    {
  17.       string strMessage = string. Empty;
  18.       //code to convert Exception’s stack trace and message to user readable format.
  19.       ….
  20.       ….
  21.       return strMessage;
  22.    }
  23. }

A client class exports data from many files to a database.

  1. public class DataExporter
  2. {
  3.    public void ExportDataFromFile()
  4.    {
  5.    try {
  6.       //code to export data from files to database.
  7.    }
  8.    catch(Exception ex)
  9.    {
  10.       new ExceptionLogger().LogIntoFile(ex);
  11.    }
  12. }
  13. }

Looks good. We sent our application to the client. But our client wants to store this stack trace in a database if an IO exception occurs. Hmm… okay, no problem. We can implement that too. Here we need to add one more class that provides the functionality to log the stack trace into the database and an extra method in ExceptionLogger to interact with our new class to log the stack trace.

  1. public class DbLogger
  2. {
  3.    public void LogMessage(string aMessage)
  4.    {
  5.       //Code to write message in database.
  6.    }
  7. }
  8. public class FileLogger
  9. {
  10.    public void LogMessage(string aStackTrace)
  11.    {
  12.       //code to log stack trace into a file.
  13.    }
  14. }
  15. public class ExceptionLogger
  16. {
  17.    public void LogIntoFile(Exception aException)
  18.    {
  19.       FileLogger objFileLogger = new FileLogger();
  20.       objFileLogger.LogMessage(GetUserReadableMessage(aException));
  21.    }
  22.    public void LogIntoDataBase(Exception aException)
  23.    {
  24.       DbLogger objDbLogger = new DbLogger();
  25.       objDbLogger.LogMessage(GetUserReadableMessage(aException));
  26.    }
  27.    private string GetUserReadableMessage(Exception ex)
  28.    {
  29.       string strMessage = string.Empty;
  30.       //code to convert Exception’s stack trace and message to user readable format.
  31.       ….
  32.       ….
  33.       return strMessage;
  34.    }
  35. }
  36. public class DataExporter
  37. {
  38.    public void ExportDataFromFile()
  39.    {
  40.       try {
  41.          //code to export data from files to database.
  42.       }
  43.       catch(IOException ex)
  44.       {
  45.          new ExceptionLogger().LogIntoDataBase(ex);
  46.       }
  47.       catch(Exception ex)
  48.       {
  49.          new ExceptionLogger().LogIntoFile(ex);
  50.       }
  51.    }
  52. }

Looks fine for now. But whenever the client wants to introduce a new logger, we need to alter ExceptionLogger by adding a new method. If we continue doing this after some time then we will see a fat ExceptionLogger class with a large set of methods that provide the functionality to log a message into various targets. Why does this issue occur? Because ExceptionLogger directly contacts the low-level classes FileLogger and and DbLogger to log the exception. We need to alter the design so that this ExceptionLogger class can be loosely coupled with those class. To do that we need to introduce an abstraction between them, so that ExcetpionLogger can contact the abstraction to log the exception instead of depending on the low-level classes directly.

  1. public interface ILogger
  2. {
  3.    public void LogMessage(string aString);
  4. }

Now our low-level classes need to implement this interface.

  1. public class DbLogger: ILogger
  2. {
  3.    public void LogMessage(string aMessage)
  4.    {
  5.       //Code to write message in database.
  6.    }
  7. }
  8. public class FileLogger: ILogger
  9. {
  10.    public void LogMessage(string aStackTrace)
  11.    {
  12.       //code to log stack trace into a file.
  13.    }
  14. }

Now, we move to the low-level class’s intitiation from the ExcetpionLogger class to the DataExporter class to make ExceptionLogger loosely coupled with the low-level classes FileLogger and EventLogger. And by doing that we are giving provision to DataExporter class to decide what kind of Logger should be called based on the exception that occurs.

  1. public class ExceptionLogger
  2. {
  3.    private ILogger _logger;
  4.    public ExceptionLogger(ILogger aLogger)
  5.    {
  6.       this._logger = aLogger;
  7.    }
  8.    public void LogException(Exception aException)
  9.    {
  10.       string strMessage = GetUserReadableMessage(aException);
  11.       this._logger.LogMessage(strMessage);
  12.    }
  13.    private string GetUserReadableMessage(Exception aException)
  14.    {
  15.       string strMessage = string.Empty;
  16.       //code to convert Exception’s stack trace and message to user readable format.
  17.       ….
  18.       ….
  19.       return strMessage;
  20.    }
  21. }
  22. public class DataExporter
  23. {
  24.    public void ExportDataFromFile()
  25.    {
  26.       ExceptionLogger _exceptionLogger;
  27.       try {
  28.          //code to export data from files to database.
  29.       }
  30.       catch(IOException ex)
  31.       {
  32.          _exceptionLogger = new ExceptionLogger(new DbLogger());
  33.          _exceptionLogger.LogException(ex);
  34.       }
  35.       catch(Exception ex)
  36.       {
  37.          _exceptionLogger = new ExceptionLogger(new FileLogger());
  38.          _exceptionLogger.LogException(ex);
  39.       }
  40.    }
  41. }

We successfully removed the dependency on low-level classes. This ExceptionLogger doesn’t depend on the FileLogger and EventLogger classes to log the stack trace. We don’t need to change the ExceptionLogger’s code any more for any new logging functionality. We need to create a new logging class that implements the ILogger interface and must add another catch block to the DataExporter class’s ExportDataFromFile method.

  1. public class EventLogger: ILogger
  2. {
  3.    public void LogMessage(string aMessage)
  4.    {
  5.       //Code to write message in system’s event viewer.
  6.    }
  7. }

And we need to add a condition in the DataExporter class as in the following:

  1. public class DataExporter
  2. {
  3.    public void ExportDataFromFile()
  4.    {
  5.       ExceptionLogger _exceptionLogger;
  6.       try {
  7.          //code to export data from files to database.
  8.       }
  9.       catch(IOException ex)
  10.       {
  11.          _exceptionLogger = new ExceptionLogger(new DbLogger());
  12.          _exceptionLogger.LogException(ex);
  13.       }
  14.       catch(SqlException ex)
  15.       {
  16.          _exceptionLogger = new ExceptionLogger(new EventLogger());
  17.          _exceptionLogger.LogException(ex);
  18.       }
  19.       catch(Exception ex)
  20.       {
  21.          _exceptionLogger = new ExceptionLogger(new FileLogger());
  22.          _exceptionLogger.LogException(ex);
  23.       }
  24.    }
  25. }

Looks good. But we introduced the dependency here in the DataExporter class’s catch blocks. Yeah, someone must take the responsibility to provide the necessary objects to the ExceptionLogger to make the work done.

Let me explain it with a real world example. Suppose we want to have a wooden chair with specific measurements and the kind of wood to be used to make that chair from. Then we can’t leave the decision making on measurements and the wood to the carpenter. Here his job is to make a chair based on our requirements with his tools and we provide the specifications to him to make a good chair.

So what is the benefit we get by the design? Yes, we definitely have a benefit with it. We need to modify both the DataExporter class and ExceptionLogger class whenever we need to introduce a new logging functionality. But in the updated design we need to add only another catch block for the new exception logging feature. Coupling is not inherently evil. If you don’t have some amount of coupling, your software will not do anything for you. The only thing we need to do is understand the system, requirements and environment properly and find areas where DIP should be followed.

Great, we have gone through the all five SOLID principles successfully. And we can conclude that using these principles we can build an application with tidy, readable and easily maintainable code.

Here you may have some doubt. Yes, about the quantity of code. Because of these principles, the code might become larger in our applications. But my dear friends, you need to compare it with the quality that we get by following these principles. Hmm, but anyway 27 lines are much less than 200 lines. I am not saying that these principles should be followed 100%, you need to draw a Yellow so that you can hold the control over the things like quality and delivery to maintain their balance.

This is my little effort to share the uses of SOLID principles. I hope you enjoyed this article.

Images courtesy :http://Lostechies.com

Thank you,

Damu