I made the following code example to learn how to use a generics method signature.
In order to get a Display() method for both Customer and Employee
I like to think of the Single Responsibility Principle as an implementation of separation of duties. Before I start splitting my classes as you have, I try to think of what each class should be responsible for.
Your classes are quite simple and lend themselves well to an abstract class with an implemented Print() and Save() functions as you mentioned. I would tend to keep that design over your current one.
However, if printing and saving were more complicated tasks which might be performed in different ways then a dedicated Printer or Saver class would be warranted, since that responsibility is now more complex. The 'complexity' threshold for making a new class is very subjective and will depend on the exact situation, but in the end, the code is just an abstraction for us lowly humans to understand, so make it such that it's the most intuitive.
You Container class is a little misleading. It doesn't actually 'contain' anything. It actually implements the Factory Method Pattern and would benefit from being named a factory.
Also, your PersonDisplayer is never instantiated and can provide all of its functionality through static methods, so why not make it a static class? It's not uncommon for utility classes such as Printers or savers to be static. Unless you have a need to have separate instances of a printer with different properties, keep it static.