Como melhorar o programa que cria formas geométricas com alta coesão e baixo acoplamento

Estou desenvolvendo o programa em anexo, mas preciso que ele tenha alta coesão e baixo acoplamento. Estava pensando em criar uma classe apenas para gerar a apresentação dos resultados, mas o problema é que fica muito acoplado pelo fato dos cálculos da área e do perímetro entre retângulo, quadrado e circunferência serem diferentes. Estou em dúvida se realizei da melhor forma. Gostaria de sugestões e com algum exemplo, ainda estou iniciando e às vezes tenho dificuldade de entender o que me dizem.

public class BrincandoComAsFigurasGeometricas {
  
  public static void main(String[] args) throws Exception {
    // Inicializando as variaveis
    String nomeRetangulo = "Retângulo";
    int alturaDoRetangulo = 2;
    int larguraDoRetangulo = 4;
    
    String nomeQuadrado = "Quadrado"; 
    int ladoDoQuadrado = 2;
    
    String nomeCirculo = "Circulo";
    int raioDoCirculo = 3;
    
    // Criando Retângulo
    try {
      Quadrilatero retangulo = new Quadrilatero(nomeRetangulo, alturaDoRetangulo, larguraDoRetangulo);
      retangulo.calcularArea();
      retangulo.calcularPerimetro();
      System.out.println("Retângulo: ");
      System.out.println(retangulo.mostrarArea());
      System.out.println(retangulo.mostrarPerimetro());
      System.out.println(retangulo.mostrarCaracteristicas());
    } catch (Exception e) {
      throw new Exception(e);
    }
    
    // Criando Quadrado
    try { 
      Quadrilatero quadrado = new Quadrilatero(nomeQuadrado, ladoDoQuadrado, ladoDoQuadrado);
      quadrado.calcularArea();
      quadrado.calcularPerimetro();
      System.out.println("\nQuadrado: ");
      System.out.println(quadrado.mostrarArea());
      System.out.println(quadrado.mostrarPerimetro());
      System.out.println(quadrado.mostrarCaracteristicas());
    } catch (Exception e) {
      throw new Exception(e);
    }
    
    // Criando Circulo
    try {
      Circulo circulo = new Circulo(nomeCirculo, raioDoCirculo);
      circulo.calcularArea();
      circulo.calcularPerimetro();
      System.out.println("\nCirculo: ");
      System.out.println(circulo.mostrarArea());
      System.out.println(circulo.mostrarPerimetro());
      System.out.println(circulo.mostrarCaracteristicas());		
    } catch (Exception e) {
      throw new Exception(e);
    }
  }
}
public interface InterfaceFormas{
  
  public int calcularArea();
  public int calcularPerimetro();	
  public String mostrarArea();
  public String mostrarPerimetro();
  public String mostrarCaracteristicas();
}
public class Circulo implements InterfaceFormas {
  
  String nome;
  int raio;
  
  public Circulo(String nome, int raio) {
    this.nome = nome;
    this.raio = raio;
  }

  public String getNome() {
    return nome;
  }

  public void setNome(String nome) {
    this.nome = nome;
  }

  public int getRaio() {
    return raio;
  }

  public void setRaio(int raio) {
    this.raio = raio;
  }

  @Override
  public int calcularArea() {		
    return (int) (2 * 3.14 * getRaio() * getRaio());
  }

  @Override
  public int calcularPerimetro() {
    return (int) (2 * 3.14 * getRaio());
  }

  @Override
  public String mostrarArea() {
    String toString = "";

    if (getRaio() > 0) { 
      toString +=  "A área deste " + getNome() + " é: " + calcularArea() + "\n";
    } else {
      toString += "O valor que vc forneceu não representa nenhuma figura geométrica conhecida!\n";
    }

    return toString;
  }

  @Override
  public String mostrarPerimetro() {
    String toString = "";

    if (getRaio() > 0) { 
      toString +=  "O perimetro deste " + getNome() + " é: " + calcularPerimetro() + "\n";
    } else {
      toString += "O valor que vc forneceu não representa nenhuma figura geométrica conhecida!\n";
    }

    return toString;
  }

  @Override
  public String mostrarCaracteristicas() {
    String toString = "";

    if (getRaio() > 0) {
      toString +=  "O circulo criado tem raio de tamanho: " + getRaio() + "\n"; 
    } else {
      toString += "O valor que vc forneceu não representa nenhuma figura geométrica conhecida!\n";
    }

    return toString;
  }
}
public class Quadrilatero implements InterfaceFormas{
  
  private String nome;
  private int altura;
  private int comprimento;
  
  public Quadrilatero(String nome, int altura, int comprimento) {
    this.setNome(nome);
    this.setAltura(altura);
    this.setComprimento(comprimento);
  }

  public String getNome() {
    return nome;
  }
  
  public void setNome(String nome) {
    this.nome = nome;
  }
  
  public int getAltura() {
    return altura;
  }
  
  public void setAltura(int altura) {
    this.altura = altura;
  }
  
  public int getComprimento() {
    return comprimento;
  }
  
  public void setComprimento(int comprimento) {
    this.comprimento = comprimento;
  }

  @Override
  public int calcularArea() {		
    return getAltura() * getComprimento();		
  }

  @Override
  public int calcularPerimetro() {
    return 2 * getAltura() + 2 * getComprimento();
  }

  @Override
  public String mostrarArea() {
    String toString = "";

    if (getAltura() > 0 && getComprimento() > 0) { 
      toString +=  "A área deste " + getNome() + " é: " + calcularArea() + "\n";
    } else {
      toString += "O valor que vc forneceu não representa nenhuma figura geométrica conhecida!\n";
    }

    return toString;
  }

  @Override
  public String mostrarPerimetro() {
    String toString = "";

    if (getAltura() > 0 && getComprimento() > 0) { 
      toString +=  "O perimetro deste " + getNome() + " é: " + calcularPerimetro() + "\n";
    } else {
      toString += "O valor que vc forneceu não representa nenhuma figura geométrica conhecida!\n";
    }

    return toString;
  }

  @Override
  public String mostrarCaracteristicas() {
    String toString = "";
    toString +=  "O " + getNome() + " criado tem altura de tamanho: " + getAltura() + 
    " e largura de tamanho: " + getComprimento() + "\n"; 
    
    return toString;
  }	
}